JSR 286

 

+

Search Tips   |   Advanced Search

 

 

Building composite applications using coordination

The coordination features of JSR 286 are based on a loosely coupled publish/subscribe model that does not require that the different portlet developers know about each other's work. At development time, you define only the data that a portlet understands, and the actual connections between portlets are created at deployment or runtime.

JSR 286 defines two different means for coordination...

Events Support for active notifications with action capabilities to the portlet.
Public render parameters Support for sharing of view state across portlets.

 

Events

JSR 286 allows portlets to send and receive events using a loosely coupled model where the portal application acts as broker between the different portlets and distributes the events.

Portlet 1 defines that it can publish some event A in its portlet.xml deployment descriptor.

Portlets 2 and 3 define that they are able to receive event A in their portlet.xml deployment descriptors.

The portal administrator or business user has now put these three portlets on a page and created connections between the sending portlet Portlet 1 and the receiving portlets Portlet 2 and Portlet 3. Now the user interacts with Portlet 1 and triggers an action on Portlet 1. As a result of this action, the portlet issues event A. After the portlet has finished its action processing the event broker component, which is typically part of a portal runtime, it looks for targets of this event.

In our sample, there is a connection between Portlet 1 and Portlets 2 and 3 for this event. Thus, the event broker calls the processEvent life cycle method on Portlet 2 and 3 with event A as payload. After the action and event processing have finished, the rendering lifecycle starts and the portlets can create new markup based on the state updates that occurred in the action and event phases.

The sample portlet OrdersPortlet is a JSR 286 event render portlet...

JSR 286 does not specify how connections for portlet coordination are defined and managed. Typically, connections are defined explicitly at page creation time, or they are automatically inferred by the portal at runtime.

In portlet.xml provide the general event definition and then reference this definition in the portlet section with tags that specify that the portlet can either receive or send this event. Each event is uniquely identified by an XML qualified name (QName), which is the mechanism for using unique names in XML

QNames have a Java representation as the standard library class...

javax.xml.namespace.QName

A QName consists of a namespace (for example, http://www.ibm.com) and a local part (for example, myEvent).

 <portlet>

  <supported-publishing-event>

    <qname xmlns:x="http://com.ibm/portal/portlets/ns">
      x:city
    </qname>

  </supported-publishing-event>

 </portlet>

 <event-definition>

  <qname xmlns:x="http://com.ibm/portal/portlets/ns">
    x:city
  </qname>

  <value-type>java.lang.String</value-type>

 </event-definition>

You can specify a full QName for each event, or if you use the same namespace for many events, you can declare it as the default namespace and then specify only the local part of each event.

In addition to the name, you need to specify the type of the event payload to allow the portal application to serialize / deserialize the payload in the case that the portlets are using different classloaders or may run as remote portlets using WSRP. Thus, the requirement for the payload is that it is either a simple type (for example, String) or that it needs to be Java serializable and Java Architecture for XML Binding (JAXB) serializable. Because remote WSRP portlets may not be written in Java, we need a more general mechanism than Java serialization. JAXB allows serializing Java objects into XML; all you have to do is annotate your Java object accordingly.

You can send an event using the setEvent method on the ActionResponse or the EventResponse.

To receive events you need either to extend GenericPortlet or to implement the EventPortlet interface yourself.

Extending GenericPortlet has the additional advantage of being able to use annotations for denoting special methods for handling events, processing actions, and rendering a specific portlet mode.

 @ProcessEvent(qname="{http://com.ibm/portal/portlets/ns}city")

 public void cityEvent(EventRequest request, EventResponse response )
                        throws IOException, PortletException 
 { 

  Event ev = request.getEvent();

  if ( ev.getValue().equals("Orlando") ) 
  {
     ...
  }
 }
 

 

Public render parameters

Public render parameters allow sharing of request parameters across different portlets or other artifacts, like themes or portal pages in IBM WebSphere Portal. Defining a public render parameter is very similar to defining an event. You specify a unique QName and optional alias names in the portlet.xml deployment descriptor. The QName and alias names are used to connect public render parameters of different portlets. You can view these public render parameters as a shared storage area where all participating portlets can set and get the values.

This example shows where a navigator portlet and a content portlet share the same public parameter docid. After you click a link in the navigator portlet that sets the docid to document3, the content portlet displays the content of that document because its view state (the ID of the current document) has been changed from an external influence. Note that public render parameters can be stored in the URL, as in the WebSphere Portal implementation, and thus you can bookmark pages and use the browser's Back and Forward buttons.

How can the portlet get access to these public render parameters? In the portlet itself, you access the public render parameters with the same methods that are used to handle ordinary render parameters defined in version 1.0. Given that these methods take only Strings as keys and not QNames you need to define an identifier in the portlet.xml that you can then use inside the portlet code for addressing the public render parameter.

In most cases, the portlet code does not actually need to change whether a render parameter is public or private, so you can enable your portlet for coordination by simply declaring those parts of the view state information in the portlet.xml that can reasonably be set from external sources as public parameters. To specifically access only public render parameters in your code, then you use the method getPublicParameterMap on the request.

 

Serving resources

In the first version of the Java Portlet Specification, you could not serve dynamically generated resources directly through the portlet. Instead, you needed an additional servlet that served the resources. This limitation had some disadvantages:

One advantage of serving resources through a servlet is less overhead because the request does not have to pass through the additional portal framework, for example, when serving large media streams.

Version 2.0 now introduces a new URL type, resource URLs. Resource URLs trigger the lifecycle method, serveResource, on the ResourceServingPortlet interface that you can leverage to create dynamic resources directly in the portlet.

 

Resource URLs

You must have a resource URL to trigger the new serveResource life-cycle method. Let's start by looking into the details of the new resource URL.

Resource URLs can be created similar to other portlet URLs, with the method createResourceURL on the RenderResponse and ResourceResponse, for example,

ResourceURL url = response.createResourceURL();

Now you can set parameters on the URL as you do for other portlet URLs. You receive these parameters in the serveResource call. Note that ResourceURLs cannot set new render parameters, portlet mode, or window state. This restriction occurs because serveResource calls do not generate a full new portal page, but return the response of serveResource. Thus, the portal does not have a chance to update other parts of the page where this information may be encoded; for example, in WebSphere Portal all URLs contain this information and so need to be updated.

You can also set an additional resource ID on the resource URL that clearly identifies your resource. If you extend GenericPortlet, GenericPortlet tries to forward to that resource ID for a serveResource call. You can set the path to your resource asID, as shown here:

url.setResourceID("WEB-INF/jsp/xmlcontent.jspx");

In this case, GenericPortlet automatically dispatches to the given JSP, which can then make use of the portlet state information by including the portlet tag library. Note that static resources like GIF files packaged in your portlet WAR should normally be referenced with static resource URLs such as:

String url = response.encodeURL(request.getContextPath()+"/icons/myigif.gif");

Serving static resources using the portlet's serveResource method causes unnecessary performance overhead.

 

Serving resources

To receive a serveResource call triggered by a resource URL, you need to either implement the new ResourceServingPortlet or extend GenericPortlet, which already implements the new interface. In the serveResource call, you get a specific resource request / response pair.

The resource request has similar methods to the render request, but in addition it lets you get access to upload data. In the resource request, the parameter handling lets you have access to three different types of parameters:

The ordinary parameter access methods return the merged set of these three parameter maps. Parameters that have the same key first get the resource parameter values merged and then either the private or public render parameter values, depending on whether the parameter key was declared as a public render parameter in portlet.xml.

With resource URLs, you can leverage all HTTP methods, not only GET, as you do in render. This capability means that you can use methods such as POST or DELETE to change state in the serveResource call. These state changes, though, should be limited to private state of the portlet: portlet-scoped session data and portlet preferences. You should not modify state that affects other portlets because the portal framework does not have a chance to update other parts of the portal page for serveResource calls, so that updates to page-level state may not be visible.

NOTE: Render parameters, portlet mode, and window state fall into this category as some portal implementations, such as WebSphere Portal, store this information in the URL to support the ability to bookmark and the use of Back and Forward buttons in the browser. This implementation means that changing a render parameter, for example, requires an update of all the URLs on the page, which the portal cannot perform, as the processing of the response data on the client is done entirely by the portlet.

Because the markup returned from serveResource is not aggregated with other markup from the portal framework, the resource response allows full control over the output stream. For example, the portlet can set a HTTP status code.

 

Caching levels of resources

There are many different use cases for serving resources; therefore, there are also different requirements for the ability to cache serveResource calls. You can influence the HTTP caching behavior of resource requests with the setCacheability method on the ResourceURL. This method gives a hint to the portal about how much information the target serveResource call requires. Therefore, the portal can remove irrelevant data from the URL, which results in more stable URLs and more HTTP caching hits for resource requests. Note that setting the cacheability of a resource response makes sense only if you allow the response to be cached by specifying HTTP caching headers for the returned response. This specification means that set at least an expiration time using response.getCacheControl().setExpirationTime(). If the returned content is not user specific, you should additionally set the cache control to public scope.

JSR 286 supports the following scenarios:

In summary, these cacheability levels give you the ability to provide as many hints about the cacheability as possible to the runtime. The runtime also can enhance the cacheability of resources in other ways, for example by keeping track of the state changes on the client and re-rendering only the parts affected by state changes. An example for this is the Client-side Aggregation theme in WebSphere Portal V6.1 Beta.

 

Using Ajax

JSR 286 allows you to issue XMLHttpRequests to ResourceURLs and, on the server side, get complete access to the portlet context such as...

With the serveResource call you can...

Ajax calls go through the portal servlet and thus are under the control of the portal.

Here is the Faces Ajax sample...

 

Closing the gap to the servlet programming model

The first version of the Java Portlet Specification restricted the portlet programming model in some areas compared to the servlet programming model. This restriction was done because portlets are aggregated on a page and all concepts that assume that you are the only component on the page are not easily applied to the portlet programming model.

The second version tackles these issues and provides solutions so that the portlet programming model has nearly the same capabilities as the servlet one, plus the portlet specific extensions.

 

Cookies, document head section elements, and HTTP headers

In the first version of the Java Portlet Specification, the portlet could not contribute to sections of the portal page outside its portlet window. In the second version, you can set cookies, document head section elements (for example, HTML ones such as meta, link, or style) and HTTP headers (such as application-specific Pragma headers).

Two-part rendering life-cycle call

To overcome the problem that version 1.0 faced with contributing content outside the current portlet window, version 2.0 added a two-part render call:

  1. RENDER_HEADER part that allows the portlet to return content outside the current portlet window such as portlet title, preferred next possible portlet modes, cookies, document head section elements, and HTTP headers.

  2. RENDER_MARKUP part that allows the portlet to return its normal markup.

These two parts are needed as some portal implementations, such as WebSphere Portal, stream the page and portlet output directly to the client to avoid buffering overhead. In this case, the portlet rendering cannot add anything to the head section of the document, as the head section is already written. Thus, portlets that want to contribute to the head section or to set a portlet title should turn on the two-part rendering feature with the setting shown in listing 3 in the portlet deployment descriptor.

Listing 3. Two-part rendering lifecycle enablement

<portlet>
.
  <container-runtime-option>
 <name>javax.portlet.renderHeaders</name>
 <value>true</value>
  </container-runtime-option>
 </portlet>
 

Implementing two-part rendering is straightforward if you extend GenericPortlet and override the methods that GenericPortlet provides, such as getTitle and getHeaders. The GenericPortlet class does the rest for you.

Using cookies

You can set cookies at the response of each life-cycle method (processAction, processEvent, render, and serveResource) with this code:

response.addProperty(javax.servlet.http.Cookie cookie)

The cookie can then be accessed in all life-cycle methods using:

request.getCookies()

Using cookies in portlets is different from using cookies in servlets in these ways:

  • As mentioned, to set cookies in the render method, the renderHeaders option should be turned on and the cookies should be set in the RENDER_HEADERS part by using overriding doHeaders() of GenericPortlet, for example.

  • Cookies may not be accessible at the client because they are stored at the portal server or they are put in a different namespace when the portlet runs as a remote portlet through WSRP.

  • Cookies are not guaranteed to be shared across different portlets.

 

Request dispatcher include and forward

In the first version of the Java Portlet Specification, you had only the option of including servlets or JSPs from the render life-cycle call. The second version lets you use forward and include and lets you use them in all life-cycle methods.

This addition means that you can now dispatch to action or event logic written in a servlet, or you can do forwards when serving resources try to forward to the resource ID that you have set on a ResourceURL.

 

Leveraging the servlet life-cycle listeners

In V1.0, the servlet HttpSession listeners can be also used for the PortletSession as the PortletSession is a façe on top of the HttpSession. Version 2.0 extends the list to all servlet life-cycle listeners defined in the Java Servlet Specification V2.5, and it enforces a correspondence between portlet and servlet objects:

  • javax.servlet.ServletContextListener. For notifications about the servlet context and the corresponding portlet context.

  • javax.servlet.ServletContextAttributeListener. For notifications about attributes in the servlet context or the corresponding portlet context.

  • javax.servlet.http.HttpSessionActivationListener. For notifications about the activation or passivation of the HTTPSession or the corresponding PortletSession.

  • javax.servlet.http.HttpSessionAttributeListener. For notifications about attibutes of the HTTPSession or the corresponding PortletSession.

  • javax.servlet.http.HttpSessionBindingListener. For notifications about binding of the object to the HTTPSession or the corresponding PortletSession.

  • javax.servlet.ServletRequestListener. For notifications about changes to the HTTPServletRequest or the mirrored portlet request of the current Web application.

  • javax.servlet.ServletRequestAttributeEvent. For notifications about changes to the attributes of the HTTPServletRequest or the mirrored portlet request of the current Web application.

A servlet request listener can distinguish a plain servlet request targeted to a servlet from a wrapped servlet request targeted to a portlet by looking at the request attribute javax.portlet.lifecycle_phase. This attribute is set on a request targeted to a portlet, indicating the current portlet life-cycle phase of this request.

Access to all these life-cycle listeners gives you many hook points for managing objects related to these lifecycles, which is a great feature for many frameworks. These hook points, though, come with a cost, especially the request lifecycle listeners, which add significant processing overhead for each request. Therefore, use them with care.

 

Extending JSR 286

The expert group of JSR 286 worked to make JSR 286 extensible so that you can add features and functionality in our frameworks on top of JSR 286 in a noninvasive and container-independent manner. You can add:

Some extension points require that the container supports this extension point for the portlet to take advantage of the extension. These extension points are:

  • Request / response properties from V1.0 for setting extension properties on request or response. Version 2.0 adds several new spec-defined extension properties, such as the predefined caching properties.

  • PortletURL properties for extending the PortletURL, for example, the predefined SHARED property for ResourceURLs.

  • Container runtime options for leveraging additional container behavior, such as the two-part rendering.

Let's take a deeper look into the new features for extending the Java Portlet Specification in the next sections.

 

Portlet filters

The new portlet filter functionality allows you to plug filters around any life-cycle call of the portlet. Following the common decorator pattern, filters can do pre- or post-processing, and they can modify or wrap the request and response objects that are passed to the portlet. Typical applications of portlet filters could include these:

  • Passing information from additional sources to the portlet as attributes or parameters

  • Output filtering for security enforcement or markup compliance

  • Collecting diagnostic information

  • Bridging between Web application frameworks

WebSphere Portal V6.1, for example, uses a filter approach to augment the markup of portlets with additional semantic information for client-side click-to-action.

The portlet filter programming model is modeled on the servlet filter model:

  1. Define the filters in the deployment descriptor. This definition is done using the <filter> element, where you also need to state the life-cycle call to which the filter should be applied.

  2. Implement the corresponding Filter interface in your filter. You can also list multiple life-cycle entries and implement multiple Filter interfaces with your class.

  3. Provide a filter-mapping element where you describe to which portlets your filter should be applied (you use an asterisk as a wildcard if it should be applied to all portlets in the application).

The order of the filter-mapping elements in the deployment descriptor also defines the order of the filters that are applied to the portlet. Listing 4 shows an example for a deployment descriptor entry.

Listing 4. Portlet filter definition example

// filter declaration
<filter>
  <filter-name>PortletFilter</filter-name>
  <filter-class>com.example.PortletFilter</filter-class>
  <lifecycle>RENDER</lifecycle>
</filter>

// filter mapping  
<filter-mapping>
  <filter-name>PortletFilter</filter-name>
  <portlet-name>MyPortlet</portlet-name>
</filter-mapping>

At runtime, a filter chain of all the filters is applied to the portlet. Each filter gets the current request and response, or a wrapped version created by a preceding filter, and the filter chain. After doing its preprocessing, the filter implementation can either terminate the request processing or call the next element in the filter chain, passing in either the received request and response or additional wrappers. The last element in the filter chain is the portlet itself.

Listing 5 is an example of a filter that does some pre- and post-processing and provides the portlet with a wrapped request.

Listing 5. Example filter implementation

 public class PortletFilter implements RenderFilter {

 public void doFilter(RenderRequest req, 
 RenderResponse resp, FilterChain chain) throws .. {
 PrintWriter pw = resp.getWriter();
 pw.write("Pre-processing");

 MyRenderResponseWrapper resWrapper = 
  new MyRenderResponseWrapper(res);
 chain.doFilter(req, resWraper);

 pw.write("Post-processing");
 }
}

Note that when you use wrappers, you should stick to the wrapper pattern and only override existing behavior. Do not add new methods on the wrapper; there may be other wrappers in the chain that you don't know of, and thus the portlet may not be able to access your new method. To provide the portlet with additional capabilities, set an object providing access to these capabilities as a request attribute.

 

Portlet URL listeners

In some cases, you may want to centrally manage setting specific properties on portlet URLs or to enhance the portlet URL creation of existing portlets. One example involves implementing a mapping of resources to shared IDs. You implement this mapping only once in the listener, and the listener checks if the resource URL is targeting a resource for which a shared QName exists and then sets the SHARED property with the corresponding QName. For these use cases, the JSR 286 specification gives you the portlet URL listeners.

You need to register such a listener with the listener element in the portlet deployment descriptor, and your class needs to implement the PortalURLGenerationListener interface that defines a callback method for each type of portlet URLs: action, render, and resource.

 

Portlet-managed modes

In JSR 168, the portlet could leverage only portlet modes that are supported by the portal framework running the portlet. In some use cases, the portlet wants to offer portlet-specific functionality with the same user look-and-feel as portlet modes supported by the portal (for example, with context menus on the portlet window). For example, a ShowShoppingCart portlet mode lists all entries that you currently have in your shopping cart.

To support these use cases, JSR 286 introduces the portlet-managed modes that are not known to the portal, but are managed by the portlet itself. The portlet can declare such a mode in the portlet deployment descriptor with the code shown in listing 6.

Listing 6. Defining a custom portlet managed mode

  <custom-portlet-mode>
  <description>Show shopping cart</description>
<portlet-mode>ShowShoppingCart</portlet-mode>
 <portal-managed>false</portal-managed>
 </custom-portlet-mode>

The important point here is to set the portal-managed mode to false. This setting indicates to the portal that it should treat this mode just like the standard View mode concerning all aspects, including how it provides the portlet with preferences and from the perspective of access control. The portal should provide UI controls that allow the portlet to switch to this mode. The portlet can define localized names for the decoration using the resource bundle entry:

javax.portlet.app.custom-portlet-mode.<name>.decoration-name.

Because the portal does not know specific semantics of the portlet-managed modes, it does not know when it makes sense to present a decoration to switch to a portlet-managed mode and when it does not. Therefore, the JSR 286 specification lets you indicate for which portlet modes the portal should display UI controls as part of the render response using the method setNextPossiblePortletModes. When you use GenericPortlet, you can override getNextPossiblePortletModes, and GenericPortlet takes care of setting these in the RENDER_HEADER part. Note that you need to set this list of modes on each response and that you should enable the container runtime option javax.portlet.renderHeaders mentioned in the next section.

 

Container runtime options

Container runtime options allow the portlet to supply specific options to the portlet container that either change default behavior defined in the Java Portlet Specification or add additional behaviors. We've already seen an example of such a container runtime setting: the renderHeaders option. JSR 286 defines a list of container runtime options that are all optional except for actionScopedRequestAttributes. In addition to these options, specific portlet container implementations may provide their own options.

You indicate that your portlet requires a specific container runtime option in the deployment descriptor using the code shown in listing 7.

Listing 7. Container runtime option template

<portlet>
.
  <container-runtime-option>
 <name>NAME_OF_THE_OPTION</name>
 <value>optionally you can have one or more parameters</value>
  </container-runtime-option>
 </portlet>

These options are predefined in JSR 286:

  • javax.portlet.escapeXml. Allows you to turn off the XML escaping of portlet URLs for each default, in case you have written a JSR 168 portlet that assumes that URLs are not XML escaped and have migrated this to a JSR 286 portlet.

  • javax.portlet.renderHeaders. Enables the two-part rendering mechanism that lets you set headers in streaming-based portal implementations.

  • javax.portlet.servletDefaultSessionScope. Allows you to change the default scope of the session object provided to servlets or JSPs that are called from the portlet through forward or include from application scope to portlet scope. That way, these resources access the same session data using portlet and servlet APIs, which is particularly convenient if your JSPs are written for a servlet-based Web framework.

  • javax.portlet.actionScopedRequestAttributes. Allows Web frameworks to pass complex objects from the action or event phase to the render phase through the request. You are able to access these attributes until the next request with an action semantic (which may be an action triggered through an Action URL or an event) occurs. This feature likely is implemented by the portlet container by storing these attributes in the session. Therefore, use this feature only if you cannot avoid it, as it will probably cause some performance degradation. This option is the only one that JSR 286 requires to be supported by all containers.

Note that if you use a container runtime option and the container on which your portlet is deployed does not support this option, the container may reject the deployment of the portlet. Use these options with care if you want to develop a portlet that can run on many different portlet container implementations.

 

Backward compatibility

Java Portlet Specification V2.0 was designed to avoid breaking binary code compatibility with V1.0 and to maintain compatible behavior for all API methods. This design point means that all portlets written against the V1.0 specification should run unchanged on a V2.0 container. The only exceptions to this rule are the following slight behavior changes that normally should not break any portlets:

  • RenderResponse.setContentType is no longer required before calling getWriter or getOutputstream. Calling getWriter or getOutputstream without previously setting the content type no longer results in an IllegalStateException in V2.0.

  • getProtocol for included servlets / JSPs no longer returns null, but instead returns HTTP/1.1 in V2.0.

This backward compatibility statement also includes the deployment descriptor in which V2.0 added new entries, but did not change existing ones. This behavior means that you can turn most JSR 168 portlets into a JSR 286 portlet by changing the one line in the portlet deployment descriptor that references the schema to the new V2.0 portlet deployment descriptor schema.

In this section, we cover various smaller additions that can make your life easier and that can support new use cases, such as the new Java 5 features leveraged in the API, the new caching features, the changes in the runtime IDs that you can access, and the tag lib additions.

 

Java 5 features

JSR 286 leverages some of the new Java 5 features:

  • Using Generics

  • Introducing a new enum class for all user profile attributes

GenericPortlet also lets you use the following annotations to make dispatching life-cycle calls to specific handler code easier:

  • ProcessAction. This annotation allows you to annotate a method that should process a specific action. To allow dispatching, the Action URL needs to include the parameter javax.portlet.action.

  • ProcessEvent. This annotation allows you to annotate methods that should process specific events.

  • RenderMode. This annotation allows you to annotate methods that should render a specific portlet mode.

Listing 8 shows how you use the ProcessAction annotation.

Listing 8. Leveraging the ProcessAction annotation

 // generating the URL
 PortletURL url = response.createActionURL();
 url.setParameter(ActionRequest.ACTION_NAME, "actionA");
 url.write(output);

 // method for handling the action
 @ProcessAction(name="actionA")
 void processMyActionA(ActionRequest req, ActionResponse resp)
 throws PortletException, java.io.IOException; {
 .
 }

Because some portal implementations still depend on Java 1.4, only features that can easily be removed for Java 1.4-based platforms have been added to JSR 286. For Java 1.4-based platforms, a JAR file of the JSP 286 APIs compiled with Java 1.4 compliance settings in which the preceding features have been removed.

 

New caching features

JSR 286 adds new caching features that help making portlets scale better:

  • Public caching scope, which marks cache entries shared across users

  • Support for multiple cached views per portlet

  • Validation-based caching for validating expired cache entries

In JSR 168, all cache entries were private for each user. If you have portlets that are not customizable and don't display different content for different users, such as a news-of-the-day portlet, then you get a cache entry for each user, even if all entries are the same. Now, in JSR 286, you can tell the portlet container that the cache entry can be shared using the cache-scope element in the deployment descriptor. Thus, you can dramatically reduce the memory footprint for these portlets, as the code in listing 9 shows.

Listiing 9. Public caching scope example

<portlet>
 ...
  <expiration-cache>60</expiration-cache>
  <cache-scope>public</cache-scope>
 ...
 </portlet>
  

A portlet can also set the cache scope programmatically using a response property or the new CacheControl interface. We recommend that you do not change the cache scope programmatically, though, as it may be difficult for portlet containers to ensure that cache entries are invalidated correctly if your portlet mixes responses with cache scope public and private for the same user.

The JSR 168 specification demanded that portlet containers must invalidate the cache for each user interaction with a portlet (render or action URL), so that there could be only a single cache entry per portlet window and user. With the introduction of public render parameters and shared caching, the specification has been enhanced to allow the container to cache multiple portlet views based on render parameters. This enhancement means that you can use render parameters so that users can navigate between different views in a shared cacheable portlet while the output still remains cached. Only the action and event processing calls, which can cause side-effects that are not known to the container, now require a cache invalidation.

Validation-based caching is useful for situations in which you do not want to recompute the markup of a portlet often because it is an expensive operation. Ideally, set the expiration time to a high value so that the markup is cached for a long time. On the other hand, you may want to respond quickly to changes in your back-end system and provide an updated view of the portlet. Validation-based caching solves this dilemma: It allows you to define a short expiration time so that your portlet gets called often and can check for changes in the back-end system. If nothing has changed in the back-end system, you validate that the expired cache content is still valid to use by setting the CacheControl. setUseCachedContent(true) and setting a new expiration time. If something has changed in the back-end system, then you produce new markup and set the setting to false.

How do you know which back-end state the currently cached markup maps to? You can set a specific validation token, called ETag after the same tag in the HTTP specification, on the response. The portlet container can then provide you with this ETag in the request of the next render or serveResource call, indicating that it still has the cached content available, which can be revalidated.

An example of using validation-based caching is shown in listing 10.

Listing 10. Validation-based caching example

 protected void doView (RenderRequest request, RenderResponse response)
 throws PortletException, java.io.IOException
{
 if ( request.getETag() != null ) {  // validation request
 if ( markupIsStillValid(request) { 
  // markup is still valid
  response.getCacheControl().setExpirationTime(30);
  response.getCacheControl().setUseCachedContent(true);
  // no need to write any output
  return;
 }
 }
 // create new content with new validation tag
 response.getCacheControl().setETag(computeETag(request));
 response.getCacheControl().setExpirationTime(60);
 PortletRequestDispatcher rd = 
 getPortletContext().getPortletRequestDispatcher("jsp/view.jsp");
 rd.include(request, response);
}
 private boolean markupIsStillValid(PortletRequest request) {
 // check if backend state still matches validation token
 return computeETag(request).equals(request.getETag())
}
 private String computeETag(PortletRequest request) {
 // return some backend state indicator like a last update timestamp
}

Using validation-based caching is beneficial only if the operations for creating the markup are expensive compared to the operations for checking the back-end state. If, for example, the request to the back-end server takes up 90 percent of the time to render the portlet, it does not make sense to use validation-based caching to save only the remaining 10 percent rendering time.

 

Runtime IDs

Runtime IDs can be used to scope data that your portlet handles, so that you can avoid collisions between multiple occurrences of a portlet within the same portal, and maybe even on the same page. Most portlet API objects, for example, portlet session or portlet preferences, do implicit namespacing, but you need to do explicit namespacing when you access shared data stores or want to assign unique IDs to output elements. This practice is common for Ajax applications, which can particularly benefit from the two enhancements that version 2.0 provides in this area.

First, JSR 286 extended the lifetime of the namespace runtime ID, using the response.getNamespace method, from being valid for only one request to now being stable for the lifetime of the portlet window. This extension now allows you to use the namespace to also crate namespace form IDs if, for example, your portlet is intended to be aggregated in a forms-based portal server. You can also leverage the namespace for JavaScript function calls that are embedded in Ajax responses, and you can reuse namespaced JavaScipt functions provided by a previous render call.

Second, JSR 286 introduced a new API call that allows you to get to a unique ID for the portlet window using the request.getWindowID method. This call now allows you to use this ID as key for data that you want to create namespace per portlet window; for example, the portlet wants to cache data that it received from a back-end system per portlet window. Note, however, that no life-cycle calls are defined around the portlet window, so if you use the portlet window ID for specifying namespace entries in a persistent data store, you also need to clean up these entries yourself.

 

Taglib additions

The JSR 286 tag library has its own namespace so that new additions do not interfere with portlets using the old JSR 168 tag library. You now need to include the new tag library with:

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

The define objects tag is now enhanced and gives you the following additional variables beyond the request, response, portletConfig from the V1.0:

portletSession Access to the portlet-scoped session.
portletSessionScope Access to the portlet-scoped session attribute key/values map.
portletPreferences Access to the portlet preferences.
portletPreferencesValues Access to the portlet preferences key/values map.

Each of the URL generation tags has the following additional attributes:

copyCurrentRenderParameters For copying the current private render parameters to the URL.
escapeXml For turning the default XML escaping of the URL. As mentioned previously, in JSR 168 it was not defined if a URL is XML-escaped or not. JSR 286 now has defined the same behavior as the Java Standard Tag Library: by default all URLs are escaped, but you can turn it off using the escapeXml attribute.

For action URLs, there is also the additional name attribute that allows you to set the javax.portlet.action parameter that is evaluated by the GenericPortlet for dispatching to the ProcessAction annotated methods.

Other additions made in the JSR 286 specification include these:

  • Adding a new resourceURL tag for generating resource URLs

  • Adding the new propertyTag that can be used inside portlet URL tags for attaching properties to URLs

 

Conclusion

As you have seen, the second version of the Java Portlet Specification added a lot of new content and abilities. The specification and the APIs more than doubled compared to version 1.0. Now the Java Portlet Specification has grown up, and it allows you to implement most use cases without the need to have vendor extensions. Note that some of the features described in this article are optional, so they may not be supported on all JSR 286-compliant platforms, but the standard ensures that those platforms that provide extended capabilities do so in a consistent and well-defined manner.

The portlet programming model now provides you with events and public render parameters so that you can build larger composite applications out of your portlets and reuse your portlets in different scenarios. Finally, you get better support for Ajax-based use cases with JSR 286 as you are now able to serve resources directly though the portlet.

Resources

 

Parent topic

Understanding the basics