ShowTable of Contents
Summary: This article describes how to use RIA technologies, such as Adobe® Flex, inside portlets based on the JSR 286 specification. We focus on usability, enabling inter-portlet communication, and “re-enabling” navigational state support for Flex user interface (UI) controls.
Introduction
End users want more, and their expectations have never been higher! They want their Web-based applications to be as feature rich as their desktop applications. However, feature-rich desktop applications that leverage the capabilities offered by an operating system usually come at a price—in the form of hefty administration and management cost.
The compromise is to offload the operating-system-specific work to the browser by exposing the functionality of the desktop application in a Web application. This (theoretically) makes your application platform independent, at the expense of some of the platform-specific capabilities provided by the operating system.
To help close the gap in capabilities, a Rich Internet Application (RIA) technology can be used in the Web application. Some RIA technologies, such as Adobe Flash (and by extension Adobe Flex), are delivered to their target audience via a plug-in in a Web browser.
The plug-in provides additional, “richer” client-side runtime capabilities while still having the advantage of being (again, theoretically) platform independent. Others take the form of a JavaScriptTM library such as Dojo that provides a set of reusable and customizable rich widgets.At the end of the day, the user is still using a browser, still accessing a Web site, and is still accessing a Web-based application. With that in mind, the RIA still needs a server-side framework to provide the core pieces for not only building and managing the site but also for the services interacting with backend systems, security, etc.
An ideal framework choice is a Portal Framework, specifically, IBM WebSphere Portal, which provides the following core services:
- Role-based delivery of information and applications
- Security: Authentication and authorization
- Single sign-on: Mapping the user's WebSphere Portal identity to a backend identity
- Navigational model: Declarative rather than programmatic approach to defining the site
- Branding, which provides a consistent user experience throughout the site
However, using RIA user interface controls in a heterogeneous environment, like a portal, can introduce some challenges to the user experience. The three main areas of concern are:
- Navigational state management
- Inter-portlet communication
- Accessing portlet APIs and personalization data
For most of this article, we use Flex as our example RIA technology. Note, however, that many of these techniques can be applied to other RIA technologies as well.
Navigational state management
Navigational state refers to the representation of the Portal for a given user at any given time. It is user and session specific and includes information such as:
- selected page
- portlet render parameters
- portlet modes
- window states
Navigational state management in WebSphere Portal is what enables bookmarking and the back- and forward-button capabilities. It allows the user to interact with a portlet on Page A, place it into edit mode, navigate to Page B, and return to Page A with that portlet remaining in edit mode. In WebSphere Portal, this navigational state is encoded into every URL on the page.
Since a Flex application is controlled by the Flash player, when changes are made by the user interacting with the user interface, the browser doesn't receive notifications of these changes. Also, since the user interface in a Flex application, it is typically updated asynchronously. Therefore, the URLs on a Portal page may become “stale”.
In other words, the navigational state encoded in the URLs that were rendered on the page are no longer up-to-date. If a user were to navigate away from the page with the Flex application on it and then return, the Flex application would re-initialize as if the user had never interacted with it. For example:
Suppose user jsmith has a portlet on a page that displays weather information for a given location. He can select the location by clicking on a spot on a map that is rendered by use of a Flex component. jsmith clicks on the southwestern portion of California, views the weather, and then navigates over to the Mail page. He sees that he has an e-mail from a friend asking about the weather in southwestern California.
Well, he has already forgotten the weather forecast, so he navigates back to the page with the weather portlet. Unfortunately, the weather portlet has no idea what jsmith previously entered because the entry was done client side, and the navigational state was not encoded into the URL used to navigate to the Mail page.
The same situation arises if a portlet updates its view dynamically on the client, using a RIA JavaScript library such as Dojo. So our first task is to re-enable the navigational-state-management capability of WebSphere Portal.
Requirements
We have three requirements for our solution:
Persistence. Enable the RIA application to “remember” its state
URL addressability. A URL can be created to the portlet containing the RIA application, such that the state can be bookmarked or sent as a link to a colleague.
Ease of consumption. The server-side portlet code does not need to be aware of any alternate persistence source.
Storage
There are numerous locations that we could store our RIA application's state, including using Flash (some JavaScript libraries use Flash for client-side storage). However, there's one obvious location that would be accessible to the server-side code as well and would allow us to satisfy the second and third requirements; that is, a cookie.
Due to the fact that we are in a WebSphere Portal environment and there could be multiple instances of the same portlet on any given page, we need to take it one step further and use a namespaced cookie. This is the first aspect of our small JavaScript library that our RIA applications will use to integrate into the portal.
BUS JavaScript library
Let's define a global namespace (“bus”) for our JavaScript library and a sub-namespace (“state”) for our navigational-state-management helper functions (see listing 1). Inside the “state” namespace, we define two functions: setClientRenderParameter and getClientRenderParameter.
The Flex application calls these functions using the ExternalInterface support built into the Flash player and passes along the values that it wants to persist.
Listing 1. Defining the namespace and sub-namespace
/**
* @namespace bus.state
*/
state: {
/**
* Store a key/value pair as a cookie in a format that can be read by the PortletFilter that
* translates cookie values into render parameters.
* @function {void} ?
* @param {String} id - the portlet application ID
* @param {String} key - the name of the render parameter
* @param {String} value - the value of the render parameter
*/
setClientRenderParameter: function ( /*String*/id, /*String*/key, /*String*/value ) {
var name = id + ":" + key;
dojo.cookie( name, value, {path: "/"} );
},
/**
* Retrieves a render parameter value for the given key.
* @function {String} ?
* @param {String} id - the portlet application ID
* @param {String} key - the name of the render parameter
* @returns the value for the given key OR undefined if the key has no value associated
*/
getClientRenderParameter: function ( /*String*/id, /*String*/key ) {
var name = id + ":" + key;
return dojo.cookie( name );
}
}
/**
* @end bus.state
*/
To implement the namespacing aspect of the cookie storage solution, we need the namespace for the current portlet to be passed into our persistence functions. This means we must pass this value into the Flex application.
The WebSphere Portal 6.1 Client-Side Programming Model provides a JavaServer Pages (JSP) tag, init, that declares the portlet's identifier in a scripting variable for use in the JSP (see listing 2). We can re-use this tag to easily retrieve this value and provide it as a parameter to the Flex application.
Listing 2. Changes to the Portlet JSP
<%@ taglib uri="http://www.ibm.com/xmlns/prod/websphere/portal/v6.1/portlet-client-model"
prefix="portlet-client-model" %>
...
<portlet-client-model:init />
...
Map<String, String[]> params = new HashMap<String, String[]>();
params.put( WiredFlexPortlet.PARAM_FLEX_APP_ID, new String [] { portletWindowID } );
String [] paramNames = renderRequest.getPreferences().getValues( WiredFlexPortlet.PREF_APP_INIT_PARAMS, null );
if ( paramNames != null ) {
for ( int i = 0; i < paramNames.length; i++ ) {
if ( renderRequest.getParameter( paramNames[i] ) != null ) {
params.put( paramNames[i], new String [] { renderRequest.getParameter( paramNames[i] ) } );
}
}
}
String flexAppUrl = WiredFlexPortlet.getFlexAppUrl( renderRequest, renderResponse, selectedFlexApp, params );
In our Flex application, we can retrieve the application ID variable in our init method:
We can then use this value in our calls to setClientRenderParameter:
And voila! We have satisfied our first requirement (persistence).
Enabling URL addressability
So how do we extend this approach to satisfy the last two requirements, URL addressability and ease of consumption? One of the new capabilities added in the second version of the Portlet specification (JSR 286) is the ability to define PortletFilters, which are quite similar to ServletFilters from the Java 2 Platform Enterprise Edition (J2EE) specification.
We can use a PortletFilter to make the namespaced cookies set by our Flex application available as render parameters to the portlet. Our PortletFilter is quite straightforward:
public class ClientRenderParameterFilter implements RenderFilter {
It simply wraps the request object and executes the filter chain:
public void doFilter( RenderRequest request, RenderResponse response, FilterChain filterChain ) throws IOException,PortletException {
filterChain.doFilter( new ClientRenderParameterWrapper( request, response, supportedParams ), response );
}
One potential enhancement is to make the Filter read the supported parameters for a given portlet from the filter configuration (see listing 3). This would limit the amount of work the request wrapper has to do.
Listing 3. Filter reading the supported parameters
public void init( FilterConfig config ) throws PortletException {
String names = config.getInitParameter( INIT_PARAM_NAMES );
if ( names != null ) {
supportedParams = new ArrayList<String>();
StringTokenizer tokens = new StringTokenizer( names );
while ( tokens.hasMoreTokens() ) {
supportedParams.add( tokens.nextToken() );
}
}
}
Just as the J2EE API provides a helper class that implements the HttpServletRequest interface for use by ServletFilters, the Portlet API provides a helper class that does the same thing for PortletFilters.
We can extend the RenderRequestWrapper helper class to create a simple wrapper that reads in all the cookies and stores a modified render parameters map containing the existing render parameters, as well as any cookie name/value pairs whose names start with the current portlet instance's namespace:
public class ClientRenderParameterWrapper extends RenderRequestWrapper {
We'll do our parameter processing in the constructor, making sure to keep all existing parameters:
//Keep all existing parameters.
parameters = new HashMap<String, String[]>();
Map<String, String []> existingParams = request.getParameterMap();
if ( existingParams != null ) {
parameters.putAll( existingParams );
Next, we cycle through the cookies, looking for any that start with our current namespace, as shown in listing 4.
Listing 4. Cycle through cookies
//Cycle through the cookies.
Cookie [] cookies = request.getCookies();
Cookie current = null;
String name = null, paramName = null, ns = getNamespace( request, response );
for ( int i = 0; i < cookies.length; i++ ) {
current = cookies[i];
name = current.getName();
if ( name.startsWith( ns ) ) {
paramName = name.substring( name.indexOf( ":" ) + 1 );
//If supportedParams is null, then any namespaced cookie should be inserted into the parameter map.
if ( supportedParams == null || supportedParams.contains( paramName ) ) {
addToParameterMap( paramName, current.getValue() );
}
}
}
Finally, we must override all parameter-related methods in the wrapper and redirect them to our modified parameter map:
public String getParameter(String name)
public Map<String, String[]> getParameterMap()
public Enumeration<String> getParameterNames()
public String[] getParameterValues(String name)
Now, all we need to do is declare our filter in the portlet.xml file. This consists of two sections. One defines the filter and the second maps it to a particular portlet (see listing 5).
Listing 5. Declare filter in the portlet.xml file
<filter>
<filter-name>NumberChooserParameterFilter</filter-name>
<filter-class>com.ibm.portal.clientextensions.filter.ClientRenderParameterFilter</filter-class>
<lifecycle>RENDER_PHASE</lifecycle>
<init-param>
<name>ParameterNames</name>
<value>selectedNumber</value>
</init-param>
</filter>
...
<filter-mapping>
<filter-name>NumberChooserParameterFilter</filter-name>
<portlet-name>WiredNumberChooserPortlet</portlet-name>
</filter-mapping>
The combination of this filter and our JavaScript API permits us to write our server-side portlet code to simply look for render parameters to initialize the state of the Flex (and/or portlet) application. (See the first example in which the URL to the Flex application is created). Recall this snippet of code:
String [] paramNames = renderRequest.getPreferences().getValues( WiredFlexPortlet.PREF_APP_INIT_PARAMS, null );
if ( paramNames != null ) {
for ( int i = 0; i < paramNames.length; i++ ) {
if ( renderRequest.getParameter( paramNames[i] ) != null ) {
params.put( paramNames[i], new String [] { renderRequest.getParameter( paramNames[i] ) } );
}
}
}
Thanks to our filter, the values read here could ultimately come from either the URL or a cookie. Not only is it considered good portlet programming practice to keep the navigational state (render parameters) up to date, but it also enables the initialization value(s) to come from a URL (actually encoded into the URL).
This allows us to meet our second and third requirements: We can now address the Flex application's state with a URL that can be bookmarked or shared with a colleague.
Note that this doesn't actually encode the parameters into the navigational state as the user interacts with the application. The bookmarking capability would be surfaced by providing a link in the user interface to create the link with the correct navigational state. It does, however, provide the effect of the state being included in the URL because of the persisted values in the cookie.
It also enables such a URL to be created. This is the same approach used by the PortalWeb2 client-side aggregation (CSA) theme in WebSphere Portal 6.1. You'll also find this approach used in numerous popular Web 2.0 applications on the Web.
Limitations
Now we must consider some limitations to this approach, the biggest of which is simply the cookie size limitation enforced by the browser. However, if the render parameter names and values are purposefully kept small, this limitation should not be a show-stopper.
One solution for the cookie size issue is to use an encoding service to “shrink” the values stored in the cookies. Another possible concern is that the information stored in a cookie is visible when sent across the wire, so care must be taken with regard to what data is actually stored in the cookie.
Inter portlet communication
In the typical inter-portlet communication interaction (see figure 1), the user executes some action in Portlet A. When the action phase of Portlet A is executed, Portlet A determines that an event must be sent to Portlet B. Portlet B receives the event from Portlet A and updates its view accordingly.
Figure 1. Typical inter-portlet communication scenario

All the event processing is executed before the response is returned. In WebSphere Portal's CSA, this process works a little differently, but the end result is that the markup for each portlet affected by the event is refreshed (see figure 2).
Figure 2. Client-side aggregation inter-portlet communication

Even in CSA, the markup for a portlet gets completely replaced when an event is executed. This can be problematic when using Flex applications because the Flex application must re-initialize on every page load (or every time it's added to the HTML document). Thus, our requirements for inter-portlet communication are:
- No full page refreshes!
- Re-use WebSphere Portal infrastructure wherever possible
Since Flex applications are able to call JavaScript functions through the
ExternalInterface object, the easiest and most natural method of client-side communication is through a JavaScript API. Let's define a new sub-namespace (“broker”) on our global namespace (“bus”) that will handle receiving and sending messages between portlets (see listing 6).
Listing 6. Define a new sub-namespace and global namespace
}
/
broker: {
/
- Sets the default behavior for the broadcast method in the case that no value is specified
- for whether or not




