Understanding the Java Portlet Specification 2.0 (JSR 286): Part 2, Serving Resources and Other New Features

   
By Deepak Gothe, January 2010  

[ Part 1] [Part 2] [ Part 3]

In this three-part series, the articles describe new features available in the Java Portlet Specification 2.0 ( JSR 286) and extensions supported by OpenPortal Portlet Container 2.x.

Part 1 provides an overview of the Java Portlet Specifications and explains in detail one of the main features introduced in JSR 286:coordination between portlets. Part 2 describes the other new features introduced in JSR 286. Part 3 describes the extensions supported by OpenPortal Portlet Container 2.x.

Contents

Part 1 - Overview and Coordination Between Portlets
Part 2 - Serving Resources and Other New Features
Serving Resources
Portlet Filters
Caching
Tag Library
Setting Markup Head Elements
Using Cookies
Request Dispatcher include and forward Methods
Portlet URL Listeners
Container Runtime Options
Compatibility
For More Information
Part 3 - Extensions
 

Serving Resources

In JSR 168 (Portlet 1.0), you could not serve dynamically generated resources directly through the portlet. Instead, you had to use an additional servlet to serve the resources. In JSR 286 (Portlet 2.0), a feature named resource serving has been introduced. This enables a portlet to dynamically serve a resource.
 
Portlets can create two kinds of links to serve resources:

  • Serving static resource: You can create direct links to the static resources in the same portlet web application. These links are constructed by the portlet and are encoded with the PortletResponse.encodeURL() method. For example, static resources like JPG files packaged in the portlet WAR should be referenced with static resource URLs such as the following:
     
    String url = response.encodeURL(request.getContextPath()+"/images/image.jpg");
     
  • Serving dynamic resource: A new URL type, resourceURL, has been introduced to serve dynamic resources. The resource URL calls the new serveResource() lifecycle method of the ResourceServingPortlet interface. This way, the portlet can serve a resource that is protected by the portal security and can leverage the portlet context. Portlet Container does not render any output in addition to the content returned by the serveResource call. Thus, the serveResource() method provides more control to you by giving access to write directly on the response object, whereas portal server just acts as a proxy.  

The portlet can create resource URLs pointing back to itself in the following ways:

  • By invoking the createResourceURL() method of the RenderResponse object or ResourceResponse object
  • By using the resourceURL tag (for example, portlet:resourceURL)

When an end user invokes such a resource URL, the portlet container calls the serveResource() method of the portlet and renders the content returned by the serveResource call. You can also set a resource ID of the resource URL to enable the same serveResource() to serve multiple resources.

Using Ajax

A significant disadvantage of using Ajax with JSR 168 portlets is that you cannot make asynchronous calls to portlets through portlet URLs. But there is a workaround. Because a portlet is a Web application that can contain other resources, such as servlets and JSP pages, you can make the asynchronous requests to the resources that are bundled with the portlet. Usually an additional servlet is used to serve the resources.

The serveResource method can also be used to implement Ajax use cases by invoking the resource URL through the XMLHttpRequest(or XMLPortletRequest) in client-side JavaScript code. The client-side code of the portlet is then responsible for inserting either the markup or updating the page DOM in a nondisruptive manner for the other components on the page.

This section explains, with an example, how to serve portlet fragments by using the serveResource method.

The presentation logic for the resource serving portlet is shown in the following code example.

<%@ taglib uri="#" prefix="portlet" %>
 <portlet:defineObjects/>

 <portlet:resourceURL var="jsURL" id="js" escapeXml="false" />
 /* Load Dojo library, if it hasn't already */
 if (typeof dojo == "undefined") {
    /* build script tag */
    var script = document.createElement("script");
    script.src ="<%=renderResponse.encodeURL(jsURL.toString())%>";
    .........
 } 
 .........
 <portlet:resourceURL var="invoiceURL" id="invoice" escapeXml="false" />
 <script type="text/javascript">
 /* Use Dojo.io.bind to asynchronously get invoice content */
 function <portlet:namespace/>_loadInvoice(invoice) {
    /* If an invoice was selected */
    if (invoice != null && invoice != "null") {
        .........
        var bindArgs = { 
            url: "<%=renderResponse.encodeURL(invoiceURL.toString())%>",
            method: "POST",
            content: querystring,
            handle: function(type, data, evt) {
            if (type == "error") {
                 /* Swap returned error message */
                data = "<p style='color:red'>" + data.message + "</p>";
            }
            /* Swap returned data into div tag */
            var div = document.getElementById("<portlet:namespace/>_content");
            div.innerHTML = "";
            div.innerHTML = data;


            },
            mimetype: "text/html"
        };
    dojo.io.bind(bindArgs);
    }
 };
 

The portlet class that serves the resource is shown in the following code example.

public class InvoicePortlet extends GenericPortlet {
    .........
    @Override
    public void serveResource(ResourceRequest request, ResourceResponse response)
        throws PortletException, IOException {

        String resourceID = request.getResourceID();
        Locale locale = request.getLocale();
        if (resourceID.equals("invoice")) {
            String invoice = request.getParameter("invoice");
            if (invoice == null) {
                throw new PortletException(
                     "Required parameter, invoice, is missing.");
            } else {
                String path = "/html/" + invoice + ".html";
                String content = getContents(path, locale, true);
                PrintWriter writer = response.getWriter();
                writer.print(content);
           }
        } else if (resourceID.equals("js")) {
            String content = getContents(jsPage, locale, false);
            PrintWriter writer = response.getWriter();
            writer.print(content);
        }
    }
}
 
Sample Application: Invoice Ajax Portlet

The InvoiceAjaxPortlet sample showcases the resource-serving feature.
 
Figure 3 shows the Invoice Ajax portlet. When the user selects an invoice number in the drop-down box, an asynchronous request (Ajax call) is made by the JavaScript client to the portlet. The serveResource() method of the portlet is invoked to serve the content.

Sample Application: Invoice Ajax Portlet
Figure 3: Sample Application: Invoice Ajax Portlet
 
Caching of Resources

The resources can be cached with the setCacheability method on the resource URL. With this method the portlet can indicate that it needs only parts of the overall state, using the cache-level parameter. The different cache-level parameter values are shown here:

  • ResourceURL.FULL("cacheLevelFull") - To mark a serveResource call as fully cacheable, you need to set the cacheability on the resource URL. The resource URL does not need to contain the current state of the page or the current render parameters, portlet mode, or window state of the portlet. Thus, the browser can cache the returned markup of the serveResource call for as long as the user interacts with the current page. As a result, the portlet cannot access the portlet mode, window state, or render parameters in the serveResource call. Only URLs with a cache level FULL are allowed in the response of the serveResource call triggered by a resource URL with a cache level of FULL. URLs of the type FULL have the highest cacheability in the browser because they do not depend on any state of the portlet or page.
     
  • ResourceURL.PORTLET("cacheLevelPortlet") - The serveResource call triggered by a PORTLET resource URL has access to the portlet state consisting of the render parameters, portlet mode, and window state. URLs of the type PORTLET are cacheable on the portlet level in the browser and can be served from the browser cache for as long as the state of this portlet does not change. If you need access to the portlet state consisting of the render parameters, portlet mode, and window state, and you are not including action or render URLs in the response, you can set the cacheability level to PORTLET. This level not only allows refreshing the resource for each interaction with the portlet, but also allows using the cached resource when interacting with other portlets on the page.
     
  • ResourceURL.PAGE("cacheLevelPage") - The resource URL of the type PAGE is cacheable only on the page level and can be served only from the browser cache as long as no state on the page changes. PAGE-level cacheability is the default setting. The resource URL might contain artifacts that require knowledge of the state of the complete page, like action or render URLs, or resource URLs of type PAGE. The markup returned by such a resource URL can contain any portlet URL.
 

Portlet Filters

A portlet filter is a Java technology-based component that can be used to modify the content of the portlet request and portlet response before or after any lifecycle method of the portlet. The concept of a portlet filter is same as that of a servlet filter. The only difference is that a servlet has only one request handling method, service(), and therefore there is only one type of servlet filter. On the other hand, a portlet has four types of request handling methods and therefore there are four different types of portlet filters.
 
The portlet API defines the following interfaces for creating portlet filters:

  • javax.portlet.filter.ActionFilter - For processAction method
  • javax.portlet.filter.EventFilter - For processEvent method
  • javax.portlet.filter.RenderFilter - For render method
  • javax.portlet.filter.ResourceFilter - For serveResource method
 

Each of these filter interfaces contains a single doFilter(*Request, *Response, FilterChain chain) method, which differs in the type of request and response object. For example, the doFilter() method of ActionFilter takes instances of ActionRequest and ActionResponse objects, while the doFilter() method of RenderFilter takes instances of the RenderRequest and RenderResponse objects.

Each filter interface extends a common base interface named javax.portlet.filter.PortletFilter. This common base interface contains two methods: init(javax.portlet.filter.FilterConfig filterConfig) and destroy(). The init() method ensures that every filter has access to a FilterConfig object from which it can obtain its initialization parameters: a reference to the PortletContext that it can use, for example, to load resources needed for filtering tasks. The destroy() method signifies the end of service of the filter. The init() and destroy() methods of a portlet filter are called only once during their lifetime.

A single filter class can provide filter functionality for more than one lifecycle method. Also, a single filter can provide filter functionality for more than one portlet. Multiple filters can be associated with one lifecycle method of a portlet. The doFilter() method of a portlet filter might create customized request and response objects by using the *RequestWrapper and *ResponseWrapper classes, and by passing these wrappers to the doFilter() method of the FilterChain object.

To write a portlet filter:
  1. Write a filter class. A filter class should implement one or more of the previously mentioned four interfaces and should provide a no argument public constructor. The filter class should also override the init() and destroy() methods of the javax.portlet.filter.PortletFilter interface.
     
    public class HitCounterFilter implements RenderFilter {
        private FilterConfig filterConfig = null;
    
        public void init(FilterConfig filterConfig) throws PortletException {
            this.filterConfig = filterConfig;
        }
    
        public void doFilter(
             RenderRequest renderRequest, RenderResponse renderResponse,
             FilterChain filterChain) 
             throws IOException,PortletException {
    
             StringWriter sw = new StringWriter();
             PrintWriter writer = new PrintWriter(sw);
             writer.println();
             writer.println("===============");
             writer.println("The number of hits is: " +count);
             writer.println("===============");
             filterChain.doFilter(renderRequest, renderResponse);
        }
    
        public void destroy() {
        }
    }
    
     
  1. Define the filter and filter mapping in the portlet.xml file. This is done using the <filter> element, where you also need to specify the lifecycle call to which the filter should be applied. In the <filter-mapping> element you describe to which portlets the filter should be applied. If you want the filter to be applied to all portlets in the application, you need to use an asterisk as a wildcard.
     
    <portlet-app ........>
        <portlet>
         ......... 
        </portlet>
        <filter>
            <filter-name>HitCounterRenderFilter</filter-name>
            <filter-class>
                com.sun.portal.portlet.filter.HitCounterFilter
            </filter-class>
            <lifecycle>RENDER_PHASE</lifecycle>
        </filter>
        <filter-mapping>
            <filter-name>HitCounterRenderFilter</filter-name>
            <portlet-name>HelloWorld</portlet-name>
        </filter-mapping>
    </portlet-app>
    
     
Sample Application: Portlet Filters

The PortletFilter sample showcases the portlet filter feature.
 
The number of times the sample portlet has been accessed is logged using a filter. Access the portlet and check the application server log file. The log file shows the number of times the portlet has been accessed.

graphics4
Figure 4: Sample Application: Portlet Filters
 

Caching

In Portlet 1.0, only the Expiration cache was supported. The cache was also per user client and not shared across users. The Portlet 2.0 specification added the following new caching features:

  • Public cache scope: The cache entries are shared across users
  • Validation-based caching: Expired cache entries can be validated using ETag
Public Cache Scope

You can tell the portlet container that the cache entry can be shared across users. This can be done by setting the value of the <cache-scope> element to public. The default value is private, which behaves like the JSR 168 caching where the cache is per user. The cache scope can also be changed programmatically using a response property, such as MimeResponse.PUBLIC_SCOPE or MimeResponse.PRIVATE_SCOPE, or the new CacheControl interface, such as response.getCacheControl().setPublicScope(true).

<portlet>
     .........
    <expiration-cache>60</expiration-cache>
    <cache-scope>public</cache-scope>
     .........
</portlet>
 
Validation-Based Caching

Validation caching is supported along with Expiration caching. After the cache has expired, the portlet has the option to check if the cache is still valid and indicate that the cache can be reused. To enable this, ETag has been introduced, which is similar to how browsers that conform to HTTP 1.1 perform caching. Once the cache has expired the portlet container calls the render or serveResource method with the ETag set in the RenderRequest or ResourceRequest, which the portlet can access to check if the cache is still valid. If the cache is still valid, then the portlet can set CacheControl.setUseCachedContent(true) in the response, and the new expiration time.

protected void doView (RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    ...
    if ( request.getETag() != null ) { // validation request
        if ( markupIsStillValid(request.getETag()) ) {
            // markup is still valid
           response.getCacheControl().setExpirationTime(30);
           response.getCacheControl().setUseCachedContent(true);
           return;
        }
    }
    // create new content with new validation tag
    response.getCacheControl().setETag(someID);
    response.getCacheControl().setExpirationTime(60);
    PortletRequestDispatcher rd = 
         getPortletContext().getPortletRequestDispatcher(
         "view.jsp");
    rd.include(request, response);
}
 

The introduction of ETag ensures that the browser can be leveraged to cache resources and markup. With the new features of public sharing of caches, resource caching, validation caching, and leveraging browser caching, the performance of an aggregation of portlets by a portal can be seen to increase significantly.

 

Tag Library

The JSR 286 tag library has its own namespace. You now need to include the new tag library with the following code:
 
<%@ taglib uri="#" prefix="portlet" %>
 
The define objects tag ( <portlet:defineObjects/>) has been enhanced. The following variables are available in the JSP page when they are included from within a portlet:

  • renderRequest and renderResponse (if the JSP is included from render method)
  • resourceRequest and resourceResponse (if the JSP is included from serveResource method)
  • actionRequest and actionResponse (if the JSP is included from processAction method)
  • eventRequest and eventResponse (if the JSP is included from processEvent method)
  • portletConfig  
  • portletSession (returns an existing session or null if no session exists)
  • Map<String,Object> portletSessionScope (provides access to the portletSession attributes)
  • portletPreferences (provides access to the portlet preferences)
  • Map<String, String[]> portletPreferencesValues (provides access to the portlet preferences as a Map)

Other additions made in the Portlet 2.0 specification include the following:

  • Adding a new resourceURL ( <portlet:resourceURL>) tag for generating resource URLs
  • Adding the new propertyTag ( <portlet:property>) that can be used inside portlet URL tags for adding properties to a URL
 

Setting Markup Head Elements

The Portlet 2.0 specification allows the portlet to set HTML elements like <link>, <script>, and <meta> in the <head> section of the portal page. This can be achieved using the addProperty method on the RenderResponse with MimeResponse.MARKUP_HEAD_ELEMENT as the property name and org.w3c.dom.Element as the value.
 
Following is the code snippet.

public class HeaderPortlet extends GenericPortlet {  

    @Override
    protected void doHeaders(RenderRequest request, RenderResponse response) {
        org.w3c.dom.Element metaDescription = response.createElement("meta");
        metaDescription.setAttribute("name", "description");
        metaDescription.setAttribute(
              "content", "everything you want to know about stamps");
        response.addProperty(
              MimeResponse.MARKUP_HEAD_ELEMENT, metaDescription);

        org.w3c.dom.Element metaKeywords = response.createElement("meta");
        metaKeywords.setAttribute("name", "keywords");
        metaKeywords.setAttribute("content", "stamps, history");
        response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, metaKeywords);
    }
 ......
}
 

After the above portlet is executed, the following lines are added to the <head> section of the portal page:
 
<meta content="everything you want to know about stamps" name="description">
<meta content="stamps, history" name="keywords">

 

Using Cookies

You can set cookies on the response of each lifecycle method ( processAction, processEvent, render, and serveResource) with the following code:

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

The cookie can then be accessed in all lifecycle methods using request.getCookies().

 

Request Dispatcher include and forward Methods

In JSR 168 (Portlet 1.0), you could include servlets or JSPs from the render lifecycle call only. The Portlet 2.0 specification allows you to include servlets or JSPs from all lifecycle methods. This allows you to dispatch to action or event logic written in a servlet or JSP. And when serving resources you can use the forward method.

 

Portlet URL Listeners

Portlets can register portlet URL listeners in order to filter URLs before they get generated. For example, the portlet could use URL listeners to set the caching level of resource URLs in one central piece of code.

To register portlet URL listeners:

  1. Write a class that implements the PortalURLGenerationListener interface that defines a callback method for each type of portlet URLs: action, render, and resource.
     
    public class URLFilter implements PortalURLGenerationListener {   
    
        public void filterActionURL(PortletURL actionURL) {
    
        }
    
        public  void filterRenderURL(PortletURL renderURL) {
    
        }
    
        public  void filterResourceURL(ResourceURL resourceURL)  {
    
        }
    }
    
     
  2. Register the listener with the <listener> element in the portlet.xml file.
     
    <portlet-app ....>
        <portlet>
        .........
        </portlet>
        <listener>
            <listener-class>com.test.URLFilter</listener-class>
        </listener>
    </portlet-app>
    
     

Container Runtime Options

Container runtime options allow the portlet to define additional runtime behavior or change the default behavior as defined in the Portlet 2.0 specification, in the portlet.xml file on either the portlet application level or the portlet level with the container-runtime-option element.

Following are the runtime options that are defined in JSR 286:

  • javax.portlet.escapeXml: In JSR 168 (Portlet 1.0), the behavior for XML escaping URLs written by the tag library was undefined, and thus portlets might have been coded with the assumption that the URLs were not XML escaped. To be able to run these portlets on a JSR 286 (Portlet 2.0) container, the specification provides the javax.portlet.escapeXml container runtime option. The value of this setting can either be true for XML escaping URLs by default, or false for not XML escaping URLs by default. The default for this setting is true.
     
    <portlet>
        ...
        <container-runtime-option>
            <name>javax.portlet.escapeXml</name>
            <value>false</value>
        </container-runtime-option>
    </portlet>
    
     
  • javax.portlet.renderHeaders: Enables the two-part rendering mechanism that lets you set headers in streaming portal implementations. The default for this setting is false.
     
    <portlet>
        ...
        <container-runtime-option>
            <name>javax.portlet.renderHeaders</name>
            <value>true</value>
        </container-runtime-option>
    </portlet>
    
     
  • javax.portlet.servletDefaultSessionScope: The default for the session variable of included or forwarded servlets or JSPs is that it maps to the portlet session with application scope. Some portlets might require that the session variable of included or forwarded servlets or JSPs maps instead to the portlet session scope in order to work correctly. These portlets can indicate this by setting the container-runtime-option javax.portlet.servletDefaultSessionScope to PORTLET_SCOPE. The default for javax.portlet.servletDefaultSessionScope is APPLICATION_SCOPE.
     
    <portlet>
        ...
        <container-runtime-option>
            <name>javax.portlet.servletDefaultSessionScope</name>
            <value>PORTLET_SCOPE</value>
        </container-runtime-option>
    </portlet>
    
     
  • javax.portlet.actionScopedRequestAttributes: The servlet-based applications assume that attributes that they set in the action phase will be accessible again when starting the rendering. The portlet specification provides the render parameters for such use cases, but some applications need to transport complex objects instead of strings. For such use cases the portlet specification provides the action-scoped request attributes as a container runtime option. If this option is set to true, any complex object that is set as a request attribute in the action or event phase is available in the render phase. These attributes can be accessed by the portlets until a new action occurs.

To set request attributes as container runtime options:

  1. Add the container runtime option as shown here in the portlet.xml file.
     
    <portlet>
        ...
        <container-runtime-option>
            <name>javax.portlet.actionScopedRequestAttributes</name>
            <value>true</value>
        </container-runtime-option>
    </portlet>
    
     
  2. Any attribute set in processAction is available in render.
     
    public class PortletA extends GenericPortlet {
    
        public void processAction(ActionRequest request, ActionResponse response)
            throws PortletException, IOException {
    
            Map map = new HashMap();
            map.put("name", name);
            map.put("city", city);
            request.setAttribute("name-city", map);
        }
    
        public void doView(RenderRequest request,RenderResponse response) 
            throws PortletException, IOException {
            ........
           Map value = (Map)renderRequest.getAttribute("name-city");
        }
    }
    
     

The OpenPortal portlet container supports javax.portlet.actionScopedRequestAttributes and javax.portlet.escapeXml container runtime options.

 

Compatibility

The JSR 286 specification (Portlet 2.0) does not break binary compatibility with JSR 168 (Portlet 1.0). This means that all portlets written using the Portlet 1.0 specification can run unchanged. The only exceptions to this rule are these:

  • renderResponse.setContentType is no longer required before calling getWriter or getOutputstream. In JSR 168, calling getWriter or getOutputstream without previously setting the content type resulted in an IllegalStateException.  
  • getProtocol for included servlets or JSPs returns 'HTTP/1.1'. In JSR 168, it returned null.
 

For More Information

[ Part 1] [Part 2] [ Part 3]

Rate This Article
 
 
Comments
Terms of Use
 
Left Curve
Java SDKs and Tools
Right Curve
Left Curve
Java Resources
Right Curve
Java 8 banner (182)