DLM Processors
As enhancements are worked on for DLM like Integrated Modes support there is often a need to add server side code to handle specific URL parameters to fulfill some function. Within layout managers in general the place where such code is typically placed is within the layout manager's
processLayoutparameters() method. Often, such changes are unique to the specific structure transformation that is in use. But within layout managers and the uPortal code in general there is no knowledge of specific structure transformation concepts like tabs and columns. The infrastructure knows only about folder objects containing other folder objects, to any depth, and leaf node objects known as channels. A structure transformation then takes this generic hierarchy and transforms it into its desired representation such as all top level folders being interpreted as tabs with the next level interpreted as columns containing a nested set of channels.
This internal model of hierarchical folders was done intentionally in the design of uPortal so that it could potentially be used with whatever structure was desired that could map into this underlying, flexible model. Hence code placed within org.jasig.portal.layout.dlm.DistributedLayoutManager's processLayoutparameters() method must accomodate this underlying model often making code much more complex than it otherwise would be if structural transformation concepts could be taken into consideration. Furthermore, such custom changes usually don't make sense to contributed back to the uPortal baseline code and hence will have to be added back in and recompiled each time that the portal is upgraded to the latest
version of the uPortal code.
Parameter Processing Pipe
Starting in uPortal 2.6 a configuration based, pluggable mechanism is available in DLM allowing an institution to plug in handlers that can act on query and post parameters included in an HTTP request. These handlers can also take part in altering the SAX event stream that represents the user's layout including adding, removing, or altering layout elements without affecting their canonical structure within the DistributedLayoutManager. This pluggable handler support is provided via what is known as the Parameter Processing Pipe. Conceptually, the location of the pipe is shown in the image below.
The uPortal user interface is generated by the class org.jasig.portal.UserInstance. As part of that processing UserInstance delegates to the plugged-in LayoutManager for processing layout parameters. Then it again delegates to have the layout pushed through UserInstance's rendering mechanism as a stream of SAX events. This event stream is what drives the structure and theme transformation. Both interactions pass through the Parameter Processing Pipe. Although shown in the image as being between UserInstance and DistributedLayoutManager the pipe is actually internal
to DistributedLayoutManager and is delegated to by the processLayoutParameters() and getUserLayout() methods.
Processor Interfaces
Plugging a handler in is accomplished by implementing at least one of two new interfaces as shown in the next figure. IParameterProcessor implementations can take part in the processLayoutParameters() call. ISaxProcessor implementations can take part in modifications to the SAX event stream. If a processor implements both interfaces then it can take part in both actions. Both processor types can be plugged-in as part of a set of fixed processors. This means that they are always there and always take part in every invocation of these calls.
Alternatively, processors can be included in a Map of optional processors with each processor being registered with a key that is unique among all registered optional processors. A single optional processor can be installed into the pipe and take part in both of these invocations until another HTTP request selects a different optional processor or removes the current optional processor. Alternatively, if an optional processor implements the IOptionalProcessor
interface then it can cause itself to be removed from being the currently selected optional processor when it has completed its processLayoutParameter tasks.
Optional Processor Selection
An optional processor is selected in an HTTP request by specifying a query or posted parameter of uP_dlmPrc=someKey. The key specified should match one of the registered optional processors. When such a parameter is seen the Parameter Processing Pipe installs that optional processor prior to handling the processLayoutParameters request. At the end of processing layout parameters
by delegating to processors the Pipe checks to see if the current optional processor implements the IOptionalProcessor interface. If it does and the processor indicates by the single method of that interface that it is finished then it is removed from being the currently
selected optional processor. Such a check is made each time that processLayoutParameters() is called.
If that processor does not implement the IOptionalProcessor interface then it will remain as the currently selected optional processor until another HTTP request includes another uP_dlmPrc
parameter. If the value of that parameter is an empty String then the optional processor is removed. If it contains another key then the new processor becomes the optional processor. However, it should be noted that these processor instances are not garbage collected. They remain cached in memory until the next time that processor is needed.
Plugging in Processors
The Parameter Processing Pipe is configured by modifying the properties/dlmContext.xml file. This file has the following structure. In the default configuration two fixed processors are included. Both processors, the ThemeParamInjector and RegularViewChannelRemover implement IParameterProcessor. The former acquires the user's name via IPeron's getFullName() method and injects it into the theme stylesheet as the userName parameter. The latter handes the requests that result when the delete button is pressed on a channel when viewing the portal in regular non-layout-editing mode.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id='dlmProcessingPipe' class="org.jasig.portal.layout.dlm.processing.ProcessingPipe" singleton="false"> <property name="fixedProcessors"> <list> <bean class="org.jasig.portal.layout.dlm.providers.ThemeParamInjector" singleton="false"/> <bean class="org.jasig.portal.layout.dlm.providers.RegularViewChannelRemover" singleton="false"/> <bean class="some.other.processor.class.name" singleton="false"/> ... additional fixed processors </list> </property> <property name="optionalProcessors"> <map> <entry key="some.String"> <bean class="another.processor.class.name" singleton="false"/> </entry> ... additional entries </map> </property> </bean> </beans>
Fixed processors are added by inserting additional bean declarations as shown within the fixedProcessors list. On each bean declaration the class of the processor is specified as the value of the class attribute. Optional processors are added to the optionalProcessors map in a similar fashion by adding entries with the key attribute of the entry being the key for that processor and the class attribute of the nested bean element containing the class of the processor.
The value of the singleton attribute must be specified with care. A Parameter Processing Pipe is instantiated for each user and remains for the duration of that user's session. The specified set of processors are also instantiated. If a processor is thread safe meaning it does not contain any user specific information stored in instance variables then a single instance of that processor could be used in all pipes for all users. To do so the singleton attribute should be set
to true. If the processor is not thread safe and maintains user or state information then its singleton attribute should contain a value of false.
Sample Processors
There are two sample processors included with the Parameter Processing Pipe implementation code that can be plugged in to portray the flexibility and power had by processors. These sample processors are found in the following two classes.
org.jasig.portal.layout.dlm.processing.ExampleBookmarksRemover org.jasig.portal.layout.dlm.processing.ExampleStickyTabEnforcer
The first sample processor is the ExampleBookmarksRemover. It implements the ISaxProcessor interface. When in place either as a fixed processor or as the currently selected optional processor it watches for SAX events for a channel with a name of Bookmarks. All such events are stripped from the SAX event stream and log entries are made of such activity. The result is that
any instances of the Bookmarks channel do not appear anywhere in the portal's user interface although the channel is still included in the layout. In a similar fashion whole new groups of elements can be injected into the event stream to incorporate layout that is not part of the internal layout built of user persisted elements and fragments.
The target elements used in the Integrated Modes approach with other layout managers is an example. Such target elements typically containing attributes like parentID and nextID and are injected between regular layout elements. When these elements are seen by the theme stylesheet they are used to add icons showing where new elements like channels can be inserted or where a selected element can be moved. A suitable processor could then be targeted as part of the link for icons allowing the appropriate actions to be taken in DistributedLayoutManager.
Alternatively, a processor could dynamically add attributes like up, down, left, or right to each channel, column, or tab as applicable to portray in what direction a particular element can be moved by the user. And again, the theme stylesheet could provide suitable icons to
portray that ability and the URL to hit to accomplish that move.
The second sample processor is the ExampleStickyTabEnforcer. It implements the IParameterProcessor interface and watches for a folder with a name of Sticky Tab. When in place either as a fixed processor or as the currently selected optional processor it watches for a folder with a name of Sticky Tab. If seen then it injects the ID of that tab into the structure stylesheet as the value of the activeTab parameter. The effect is that the Sticky Tab will be the selected tab and no other tab can be selected. Such an approach could be used to force a specific tab to maintain focus until some link is selected by the user that could delegate to another processor causing the sticky tab functionality to be removed.
There are many possible layout management approaches and features that could be provided by suitable URL handling processor sets and the related structure and theme transformation stylesheets. The pipeline's pluggability allows for all of these tightly coupled, collaborating pieces to be developed in a modular fashion and added in as desired. For example, these groupings could be treated as separate DLM UI projects in the repository. Then based on the features offered by a set, institutions could elect to drop that set into their version of the portal, register those stylesheets, and change user accounts to use those stylesheets.
mrb 6/16/2007