Skip to main content

Understanding the Java Portlet Specification 2.0 (JSR 286)

This article describes new features available in the Java Portlet Specification 2.0 (JSR 286) and also extensions supported by OpenPortal Portlet Container 2.x.

Contents

  • Serving Resources
  • Portlet Filters
  • Caching
  • Tag Library
  • Setting Markup Head Elements
  • Using Cookies
  • Request Dispatcher include and forward
  • Portlet URL Listeners
  • Container runtime options
  • Compatibility
  • Extensions
  • References
  • Download

  • Overview
    Portlets are web-based components that enable integration between applications and portals and thus delivery of applications on portals.

    The Java Portlet Specification achieves interoperability among portlets and portals by defining the APIs for portlets. The Java Portlet Specification 1.0, Java Specification Request (JSR) 168, was released in October 2003. This brought a world of difference. By adhering to the standards, you can build portlets that can run in portals, irrespective of their vendors.

    Since its release in 2003, JSR 168 has gone through many real-life tests in portal development and deployment. For all that the community has identified "gaps," standards take time to evolve and become available to the public. Meanwhile, many portal vendors have been filling those gaps with their own custom solutions, which unfortunately cause portlets to be not portable.

    In February 2006, the JSR 286 Expert Group was formed to start work on Java Portlet Specification 2.0 and the final version was aproved on June 2008. This article describes the new features that are available in JSR 286 along with a sample application to demonstrate how to write portlets with these features.

    The OpenPortal Portlet Container 2.x provides an implementation of the Java Portlet Specification 2.0. In addition to this, it also provides a portlet driver, a lightweight portlet rendering environment. This driver simulates some capabilities of a typical portal product like the Web Space Server, Liferay Portal.


    Coordination between portlets
    In order to provide coordination between portlets the Java Portlet Specification, JSR 286 introduces the following mechanisms

    Events: portlet events that a portlet can receive and send
    Public Render Parameters: render state that can be shared between portlets

    Events

    In JSR 168(Portlet 1.0), the only way to achieve eventing between portlets was via portlet session. This was possible between portlets that are in the same webapplication. JSR 286(Portlet 2.0) defines a lifecycle for events and eventing is possible between portlets that are in different webapplications.

    An event is a life cycle operation that occurs before the rendering phase. Events can be described as a loosely coupled, brokered means of communication between portlets. Events allow portlets to respond on actions or state changes not directly related to an interaction of the user with the portlet.

    A portlet can declare events in its deployment descriptor by using the event-definition element in the portlet application section. In the portlet section, each portlet specifies the events it would like to publish through the supported-publishing-event element and the events it would like to process through the supported-processing-event element.

    The supported-publishing-event and supported-processing-event elements must reference the event name defined in the portlet application section in a event-definition element.

    The portlet creates events using the setEvent() method during action processing. This will be processed by the portlet container after the action processing has finished. Portlets can also create events during event phase by calling setEvent() method on EventResponse.

    To receive events, the portlet must implement the javax.Portlet.EventPortlet interface. The portlet container calls the processEvent() method for each event targeted to the portlet with an EventRequest and EventResponse object. The portlet can access the event that triggered the current process event call by using the EventRequest.getEvent() method. This method returns an object of type Event encapsulating the current event name and value.

    Event names are represented as QNames to identify them uniquely. The event name can be retrieved by using the getQName() method that returns the complete QName of the event, or by using the getName() method that only returns the local part of the event name. The value of the event must be based on the type defined in the deployment descriptor.

    To create portlets that use the event feature, follow these steps:

    1. Declare the events that will be published/processed in the portlet.xml file at the portlet application level. The event definition must specify a name and optionally a object type.

    Note: The object must be serializable and must be annotated with the XML root element.

    <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
                 http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
                 id="myPortletApp" version="2.0">

        <portlet>
         . . .
         . . .
        </portlet>
        <event-definition>
            <qname xmlns:x="http:sun.com/mapevents">x:Continent</qname>
            <value-type>com.sun.portal.portlet.mapevent.Continent</value-type>
        </event-definition>
    </portlet-app>
    @XmlRootElement 
    public class Continent implements Serializable {

        private String name;
        private String description;

        public Continent() {
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }
    }
                 
    2. If the portlet wants to publish a event, specify the event name defined above in the portlet section of the portlet.xml file.

    <portlet-app ......>
        <portlet>
             <portlet-name>ContinentPortlet</portlet-name>
             <display-name>ContinentPortlet</display-name>
             <portlet-class>com.sun.portal.portlet.event.ContinentPortlet</portlet-class>
             . . .
             <supported-publishing-event>
                 <qname xmlns:x="http:sun.com/events">x:Continent</qname>
             </supported-publishing-event>
        </portlet>
        . . .
    </portlet-app>


    3. 
    If the portlet wants to process a event, specify the event name defined above in the portlet section of the portlet.xml file.

    <portlet-app ......>
        <portlet>
            <portlet-name>ContinentMapPortlet</portlet-name>
            <display-name>ContinentMapPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.event.ContinentMapPortlet</portlet-class>
            ........
            <supported-processing-event>
                <qname xmlns:x="http:sun.com/events">x:Continent</qname>
            </supported-processing-event>
        </portlet>

        <portlet>
            <portlet-name>ContinentInfoPortlet</portlet-name>
            <display-name>ContinentInfoPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.event.ContinentInfoPortlet</portlet-class>
            ........
            <supported-processing-event>
                <qname xmlns:x="http:sun.com/events">x:Continent</qname>
            </supported-processing-event>
        </portlet>
        ........
    </portlet-app>


     
    4. Issue an event in the portlet that was specified as a supported-publishing-event event in the portlet.

    public class ContinentPortlet extends GenericPortlet {

        public void processAction(ActionRequest request, ActionResponse response)
            throws PortletException,IOException {

            QName qname = new QName("http:sun.com/mapevents" , "Continent");
            String value = request.getParameter("continent");

            Continent continent = new Continent();
            continent.setName(value);
            ResourceBundle rb = getPortletConfig().getResourceBundle(request.getLocale());
            continent.setDescription(rb.getString(value));
            response.setEvent(qname, continent);
        }

        .........
    }

    5. Process the event in the portlet that has been specified as a supported-processing-event event in the portlet.

    public class ContinentInfoPortlet extends GenericPortlet { 

        public void processEvent(EventRequest request, EventResponse response) {
            Event event = request.getEvent();
            if(event.getName().equals("Continent")) {
                Continent payload = (Continent)event.getValue();
                response.setRenderParameter("continentDescription", payload.getDescription());
            }
        }
        ........
    }
    public class ContinentMapPortlet extends GenericPortlet { 

        public void processEvent(EventRequest request, EventResponse response) {
            Event event = request.getEvent();
            if(event.getName().equals("Continent")) {
                Continent payload = (Continent)event.getValue();
                response.setRenderParameter("continentName", payload.getDescription());
            }
        }
       ........
    }

    Sample Application

    The EventingMap sample showcases the eventing feature

    Figure 1 shows the World Map, Continent Information, and Continent Map Portlets that participate in the event. Clicking on any continent in the World Map triggers an event. This event is processed by the Continent Information and Continent Map Portlets to show the relevant information. 

    Sample Application: Events
    Figure 1: Sample Application:

    Using Wildcards

         In order to use wildcard, the portlet must organize the local part of the event names in the event-definition element in a hierarchical manner using the dot(‘.’) as separator. for example: "foo.event.one". 

    Wildcards should only be used in the supported-processing-event or supported-publishing-event elements.

    If the wildcard string should match a part of a hierarchy two dots are required at the end of the wildcard string: one to denote the hierarchy and one for the wildcard, for example: "foo..".

    The event name must be part of the hierarchy, not a substring. For example "foo." matches "foo.bar", but it does not match "football"

    Consider the following example:
    <portlet-app .......>
        <portlet>
            ...
            <portlet-name>Portlet1</portlet-name>
            ...
            <supported-publishing-event>
                <qname xmlns:x="http:example.com/events">x:foo.event.</qname>
            </supported-publishing-event>
            ...
        </portlet>

        <portlet>
            ...
            <portlet-name>Portlet2</portlet-name>
            ...
            <supported-processing-event>
                <qname xmlns:x="http:example.com/events">x:foo.event.</qname>
            </supported-processing-event>
            ...
        </portlet>

        <portlet>
            ...
            <portlet-name>Portlet3</portlet-name>
            ...
            <supported-processing-event>
                <qname xmlns:x="http:example.com/events">x:foo..</qname>
            </supported-processing-event>
            ...
        </portlet>

        <portlet>
             ...
            <portlet-name>Portlet4</portlet-name>
            ...
            <supported-processing-event>
                 <qname xmlns:x="http:example.com/events">x:foo.e.</qname>
            </supported-processing-event>
            ...
        </portlet>

        <event-definition>
            <qname xmlns:x="http:example.com/events">x:foo.event.one</qname>
            <value-type>com.example.Address</value-type>
        </event-definition>

        <event-definition>
            <qname xmlns:x="http:example.com/events">x:foo.event.two</qname>
            <value-type>com.example.Address</value-type>
        </event-definition>

        <event-definition>
            <qname xmlns:x="http:example.com/events">x:foo.bar.event</qname>
            <value-type>com.example.Address</value-type>
        </event-definition>

    </portlet-app>

    • Portlet1 can publish the events x:foo.event.one and x:foo.event.two as the event definitions "x:foo.event.one" and "x:foo.event.two" begins with "x:foo.event."
    • Portlet2 can process the events x:foo.event.one and x:foo.event.two as the event definitions "x:foo.event.one" and "x:foo.event.two" begins with "x:foo.event."
    • Portlet3 can process the events x:foo.event.one, x:foo.event.two and x:foo.bar.event as the event definitions "x:foo.event.one", "x:foo.event.two" and "x:foo.bar.event" begins with the hierarchy "x:foo."
    • Portlet4 cannot process any event as there is no event that begins with the hierarchy x:foo.e. (Note: e. and event. are two different hierarchies, i.e, e. does not mean starting with e)

    Using Alias

        Alias names can be used to coordinate between two portlet applications that use different event names. Consider a portlet, PortletA that uses lastname as event name and a portlet, PortletB that uses surname as a event name. The event published by PortletA cannot be processed by PortletB as the event names are different. By adding alias name in the portlet.xml of either PortletA or PortletB, the PortletB can process the event. This can be done as follows..

    1. Add alias name(same as qname of the event of PortletB) in the portlet.xml of PortletA as follows.
    Note: If you want to add alias name in PortletB, it should be the qname of the event of PortletA. 



    PortletA:
    <portlet-app ......>
        <portlet>
            <portlet-name>PortletA</portlet-name>
            <display-name>PortletA</display-name>
            <portlet-class>com.sun.portal.portlet.PortletA</portlet-class>
             ........
            <supported-publishing-event>
                <qname xmlns:x="http:sun.com/events">x:lastname</qname>
            </supported-publishing-event>
        </portlet>
        <event-definition>
            <qname xmlns:x="http:sun.com/events">x:lastname</qname>
            <alias xmlns:x="http://sun.com/events">x:surname</alias>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

    PortletB:

    <portlet-app ...>
        <portlet>
            <portlet-name>PortletB</portlet-name>
            <display-name>PortletB</display-name>
            <portlet-class>com.sun.portal.portlet.PortletB</portlet-class>
            ........
            <supported-processing-event>
                 <qname xmlns:x="http:sun.com/events">x:surname</qname>
            </supported-processing-event>
        </portlet>
        <event-definition>
            <qname xmlns:x="http:sun.com/events">x:surname</qname>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

                   
    2. When the event "lastname" is published in PortletA, it can be processed as "surname" in PortletB.

    public class PortletA extends GenericPortlet {

        public void processAction(ActionRequest request, ActionResponse response)
            throws IOException, PortletException {
            ........
            QName qname = new QName("http://sun.com/events","lastname","x");
            response.setEvent(qname, "Doe");
            ........
        }
    }

    public class PortletB extends GenericPortlet {

        @ProcessEvent(qname = "{http://sun.com/events}surname")
        public void processEvent(EventRequest request, EventResponse response) {
            Event event = request.getEvent();
            String name = (String)event.getValue(); //name will be "Doe"
            ........
        }
    }



    Public Render Parameters

    In JSR 168(Portlet 1.0), the render parameters set in the processAction() method are available only in the render phase of the same portlet.

    By using the public render parameters feature, the render parameters set in the processAction() method of one portlet are available in render parameters of the other portlets. Using public render parameters instead of events avoids the additional process event call. The public render parameters can also be set on the render URL.

    To enable coordination of render parameters with other portlets within the same portlet application or across portlet applications, the portlet can declare public render parameters in its deployment descriptor using the public-render-parameter element in the portlet application section. Public render parameters can be viewed and changed by other portlets or components.

    In the portlet section, each portlet can specify the public render parameters to be shared through the supported-public-render-parameter element. The supported-public-render-parameter element must reference the identifier of a public render parameter defined in the portlet application section in a public-render-parameter element.

    To create portlets that use the public render parameters, follow these steps:

    1. Declare the render parameters to be shared in the portlet.xml file by setting the public render parameters at the portlet application level.

    <portlet-app ...>
        <portlet>
        ...
        </portlet>
        <public-render-parameter>
            <identifier>zip-id</identifier>
            <qname xmlns:x="http://sun.com/params">x:zip</qname>
        </public-render-parameter>
    </portlet-app>

                
    2. Specify the render parameter that the portlet would like to share in the portlet section of the portlet.xml

    <portlet-app ......>
        <portlet>
            <portlet-name>WeatherPortlet</portlet-name>
            <display-name>WeatherPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.WeatherPortlet</portlet-class>
             ........
            <supported-public-render-parameter>zip-id</supported-public-render-parameter>
        </portlet>

        <portlet>
            <portlet-name>MapPortlet</portlet-name>
            <display-name>MapPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.MapPortlet</portlet-class>
            ........
            <supported-public-render-parameter>zip-id</supported-public-render-parameter>
        </portlet>
        ........
    </portlet-app>

                   
    3. Set the render parameter in the processAction() method by using the defined public render parameter identifier as the key.

    public class WeatherPortlet extends GenericPortlet {

        public void processAction(ActionRequest request, ActionResponse response)
            throws IOException, PortletException {
            ........
            response.setRenderParameter("zip-id", zip);
            ........
        }
    }

    Sample Application

    The WeatherMap sample showcases the public render parameter feature.

    Figure 2 shows the Weather and Map portlets. The Weather portlet sets the zip which is declared as a Public Render Parameter. This parameter is supported by both Weather and Map portlets. Any change in the value of zip by Weather portlet is reflected during the render phase of both weather and map portlets. 

    Sample Application: Public Render Parameters
    Figure 2: Sample Application: Public Render Parameters

    Using Alias

        Alias names can be used to coordinate between two portlet applications that use different public render parameter identifiers. Consider a WeatherPortlet that uses zip as a public render parameter and a MapPortlet that uses pincode as a public render parameter. These two portlets cannot coordinate as the identifiers are different. By adding alias name in the portlet.xml of one of the portlet application, the portlets can coordinate with each other. This can be done as follows..

    1. Add alias name(same as qname of the public render parameter of WeatherPortlet) in the portlet.xml of MapPortlet as follows.
    Note: If you want to add alias name in WeatherPortlet, it should be the qname of the
    of the public render parameter of MapPortlet.

    WeatherPortlet:
    <portlet-app ......>
        <portlet>
            <portlet-name>WeatherPortlet</portlet-name>
            <display-name>WeatherPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.WeatherPortlet</portlet-class>
             ........
            <supported-public-render-parameter>zip-id</supported-public-render-parameter>
        </portlet>
        <public-render-parameter>
            <identifier>zip-id</identifier>
            <qname xmlns:x="http://sun.com/params">x:zip</qname>
        </public-render-parameter>
    </portlet-app>

    MapPortlet:
    <portlet-app ...>
        <portlet>
            <portlet-name>MapPortlet</portlet-name>
            <display-name>MapPortlet</display-name>
            <portlet-class>com.sun.portal.portlet.MapPortlet</portlet-class>
            ........
            <supported-public-render-parameter>pin-code</supported-public-render-parameter>
        </portlet>
        <public-render-parameter>
            <identifier>pin-code</identifier>
            <qname xmlns:x="http://sun.com/params">x:pincode</qname>
            <alias xmlns:x="http://sun.com/params">x:zip</alias>
        </public-render-parameter>
    </portlet-app>

                   
    2. When the public render parameter "zip-id" is set in WeatherPortlet, it can be obtained as "pin-code" in MapPortlet.

    public class WeatherPortlet extends GenericPortlet {

        public void processAction(ActionRequest request, ActionResponse response)
            throws IOException, PortletException {
            ........
            response.setRenderParameter("zip-id", "10025");
            ........
        }
    }

    public class MapPortlet extends GenericPortlet {

        public void doView(RenderRequest request, RenderResponse response)
            throws IOException, PortletException {
            ........
            String value = response.getParameter("pin-code"); // value is 10025
            ........
        }
    }



    Serving Resources

    In JSR168(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 JSR286(Portlet 2.0), a feature called resource serving has been introduced. This enables a portlet to dynamically serve a resource.

    Portlets can create two kinds of links to serve resources:

    1. 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 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:
      String url = response.encodeURL(request.getContextPath()+"/images/image.jpg");

    2. 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 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 where portal server just acts as a proxy.
         The portlet can create resource URLs pointing back to itself in the following ways:
    (a) By invoking the createResourceURL() method of the RenderResponse object or ResourceResponse object .
    (b) By using resourceURL tag(i.e 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 resourceURL to enable same serveResource() serving 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 resourceURL 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 update the page DOM in a nondisruptive manner for the other components on the page.

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

    The presentation logic for the resource serving portlet looks as below:  

    <%@ taglib uri="http://java.sun.com/portlet_2_0" 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 looks as below:

    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

    The InvoiceAjaxPortlet sample showcases the resource serving feature.

    Figure 3 shows the Invoice Portlet. When the user selects a 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 ResourceURL. With this method the portlet can indicate that it only needs parts of the overall state via the cache level parameter. The different cache level parameter values are

    1. 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 via a ResourceURL with a cache level FULL. URLs of the type FULL have the highest cacheability in the browser as they do not depend on any state of the portlet or page.

    2. 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.

    3. ResourceURL.PAGE("cacheLevelPage") – The resource URL of the type PAGE are only cacheable on the page level and can only be served from the browser cache as long as no state on the page changes. PAGE-level cacheability is the default setting.  The resource URL may 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 may 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 life cycle 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 the servlet filter. A portlet on the other hand 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 the above filter interface 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.

    Each filter interface extends a common base interface called javax.portlet.filter.PortletFilter. This common base interface contains two methods:
    init(javax.portlet.filter.FilterConfig filterConfig) and destroy(). The init() method makes sure that every filter has access to a FilterConfig object from which it can obtain its initialization parameters, a reference to the PortletContext which 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 life cycle method. Also, a single filter can provide filter functionality for more than one portlet. Multiple filters can be associated with one life cycle 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, follow these steps:

    1. Write a filter class. A filter class should implement one or more of the above 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() {
        }
    }

    2. Define the filter and filter mapping in the portlet.xml . 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 used 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

    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. 

    Sample Application: Portlet Filters
    Figure 4: Sample Application: Portlet Filters



    Caching


    In Portlet 1.0,  only "Expiration" cache was supported.  Also the cache is 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 <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(i.e MimeResponse.PUBLIC_SCOPEMimeResponse.PRIVATE_SCOPE) or the new CacheControl interface(i.e 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 the browsers, which conform to HTTP 1.1, perform caching. Once the cache has expired the Portlet Container calls the render/serveResource method with the ETag set in the RenderRequest/ResourceRequest, which the portlet can access and 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 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:
    <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

    The define objects tag
    (<portlet:defineObjects/>)  has been enhanced. Following variables are available in the JSP page when included from within a Portlet.
    • RenderRequest renderRequest and RenderResponse renderResponse (if the JSP is included from render method)
    • ResourceRequest resourceRequest and ResourceResponse resourceResponse (if the JSP is included from serveResource method)
    • ActionRequest actionRequest and ActionResponse actionResponse (if the JSP is included from processAction method)
    • EventRequest eventRequest and EventResponse eventResponse (if the JSP is included from processEvent method)
    • PortletConfig portletConfig
    • PortletSession portletSession (returns an existing session or null if no session exists)
    • Map<String, Object> portletSessionScope (provides access to the portletSession attributes)
    • PortletPreferences 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 includes
    • 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 URL


    Setting Markup Head Elements

    The Portlet 2.0 specification allows the portlet to set HTML elements like <link>, <script>, <meta> in the <head> section of the portal page. This can be achieved via the addProperty method on the RenderResponse with MimeResponse.MARKUP_HEAD_ELEMENT as property name and org.w3c.dom.Element as 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 will be added  <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

    In JSR168(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 do forward.


    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.
    This can be done as follows:

    1. Write a class that implements the PortletURLGenerationListener interface that defines a callback method for each type of portlet URLs: action, render, and resource.

    public class URLFilter implements PortletURLGenerationListener {   

        public void filterActionURL(PortletURL actionURL) {

        }
        public  void filterRenderURL(PortletURL renderURL) {

        }
        public  void filterResourceURL(ResourceURL resourceURL)  {

        }
    }

    2.  Register such a listener with the <listener> element in the portlet.xml

    <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 behaviour as defined in the Portlet 2.0 specification, in the portlet.xml on either the portlet application level or the portlet level with the container-runtime-option element..

    Following runtime options are defined in JSR 286.

    javax.portlet.escapeXml : In JSR168(Portlet 1.0), the behavior in regards to XML escaping URLs written by the tag library was undefined and thus portlets may have been coded with the assumption that the URLs were not XML escaped. In order to be able to run these portlets on a JSR286(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 per default, or false for not XML escaping URLs per 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 / forwarded servlets or JSPs is that it maps to the portlet session with application scope. Some portlets may require that the session variable of included / forwarded servlets or JSPs maps instead to the portlet session scope in order to work correctly. These portlets can indicate this via 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 container runtime option. If this option is set to true, any complex object 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.  This feature can be used as follows:

    1. Add the container runtime option as mentioned below in portlet.xml

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


    2. Any attribute set in processAction will be 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 spec(Portlet 2.0) does not break binary compatibility with JSR168(Portlet 1.0). This means that all portlets written against the Portlet 1.0 specification can run unchanged. The only exceptions to this rule are:
    • renderResponse.setContentType is no longer required before calling getWriter or getOutputstream. In JSR168, calling getWriter or getOutputstream without previously setting the content type resulted in an IllegalStateException.
    • getProtocol for included servlets / JSPs returns ‘HTTP/1.1’, In JSR168, it returned null.


    Extensions

    Following additional features are available as an extension in OpenPortal Portlet Container 2.x (also available in Web Space server and Liferay Portal Server as OpenPortal Portlet Container 2.x is consumed by them).

    Coordination between portlets across pages

    Eventing:

       By default portlets can send and receive events with other portlets that are on the same page. This can be changed to enable portlets to send and receive events from other portlets that are on different pages. This can be done by adding the following property in portal-ext.properties in Web Space server and Liferay Portal 5.2.x.

    portlet.event.distribution=ALL_PORTLETS

    Public Render Parameters:

       By default portlets can share render states with other portlets that are on the same page. This can be changed to enable portlets to share render states with other portlets that are on different pages. This can be done by adding the following property in portal-ext.properties in Web Space server and Liferay Portal 5.2.x.

    portlet.public.render.parameter.distribution=ALL_PORTLETS

    Preventing infinite loops in Eventing:

        The following property is used to specify the maximum number of events that can be generated from portlets to prevent infinite loops. If a portlet sends an event to other portlets, it is considered as one event generation. If the other portlets send events, that is considered as two event generations and so on. The default value is 3.

    portlet.event.max.generation=3



    Shared Session Attributes

    In order to support coordination between two portlets,  JSR286(Portlet 2.0) has introduced Events and Public Render Parameters.  If you want to pass data after serving the resource from one portlet to another portlet on a different page, Events and Public Render Parameters cannot be used. Shared Session Attributes feature has been added to support this use case.

    The portlet can define shared session attributes that can be shared across portlet web applications.  Shared session attributes are set, obtained and removed using the APPLICATION_SCOPE portlet session methods. If you want to remove an attribute, you should set it to null. Shared attributes values must be serializable and in addition must have a valid JAXB binding.

    The portlet must declare shared session attributes in the sun-portlet.xml deployment descriptor. On the application level the portlet must define the
    shared session attribute definition with the <shared-session-attribute> tag. The shared session attribute definition must contain the attribute name and type.
    The type must be a fully qualified Java class name. The shared session attribute should uniquely identify the attribute and use the Java package naming standard and character restrictions.

    To create portlets that use the shared session attributes feature, follow these steps:

    1. Declare the attributes that will be shared by the portlet in the sun-portlet.xml file at the portlet application level. The
    shared session attribute definition must specify a name and a object type.  Similarly that portlet that wants to receive the
    shared session attribute should also specify the attribute in its 
    sun-portlet.xml
    Note: The object must be serializable and must be annotated with the XML root element.

    PortletWebApplication1:

    <portlet-app-extension xmlns="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                            xmlns:sunportal="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            xsi:noNamespaceSchemaLocation="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            version="2.0">

    <portlet-name>PortletA</portlet-name>
        <shared-session-attribute>
            <name>com.foo</name>
            <value-type>com.foo.Address</value-type>
        </shared-session-attribute>
    </portlet-app-extension>

    PortletWebApplication2:

    <portlet-app-extension xmlns="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                            xmlns:sunportal="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            xsi:noNamespaceSchemaLocation="http://portlet-container.dev.java.net/xml/ns/sun-portlet.xsd"
                                            version="2.0">

    <portlet-name>PortletB</portlet-name>
        <shared-session-attribute>
            <name>com.foo</name>
            <value-type>com.foo.Address</value-type>
        </shared-session-attribute>


    </portlet-app-extension>

    @XmlRootElement
    public class Address implements Serializable {
        private String street;
        private String city;

        public void setStreet(String s) {
            street = s;
        }

        public String getStreet() {
            return street;
        }
        public void setCity(String c) { 
            city = c;
        }

        public String getCity() {
            return city;
        }
    }


    2. Set the attribute in the Portlet Session at APPLICATION_SCOPE

    PortletWebApplication1:
    public void  serveResource(ResourceRequest request, ResourceResponse response) {
        ...
        Address sampleAddress = new Address();
        sampleAddress.setStreet("myStreet");
        sampleAddress.setCity("myCity");

       PortletSession session = request.getPortletSession();
        session.setAttribute("com.foo", sampleAddress, PortletSession.APPLICATION_SCOPE);
        ....
    }
    PortletWebApplication2:
    public void  doView(RenderRequest request, RenderResponse response) {
        ...
        PortletSession session = request.getPortletSession();
        Address sampleAddress = (Address)session.getAttribute("com.foo", PortletSession.APPLICATION_SCOPE);
        ....
    }


    Portal Query Parameters

    Sometime there is a need for the portlet to access the portal query parameters. This can be done as follows.

    1. Assume that the portal page has a portlet "PortletQueryParameterPortlet" and its sun-portlet.xml looks like this

    <portlet>
        <portlet-name>PortletQueryParameterPortlet</portlet-name>
        <portal-query-parameters>
            <name>foo</name>
        </portal-query-parameters>
    </portlet>


    2. When you access the Portal with the following URL
    http://localhost:8080/portletdriver?foo=bar

    In doView() of PortletQueryParameterPortlet, if you invoke request.getParameter("foo"), it will return bar


    XMLPortletRequest

    XMLPortletRequest is a wrapper over XMLHttpRequest and shares the same syntax and semantics with XMLHttpRequest. If a portlet wants to update its UI asynchronously through resource URL, it would simply use XMLPortletRequest instead of the XMLHttpRequest. 

    Here is an example on how to use XMLPortletRequest.
    <script type="text/javascript" 
        src="<%=renderRequest.getContextPath()%>/js/XMLPortletRequest.js">
    </script>
    <script>
        var portletRequest = new XMLPortletRequest("<portlet:namespace/>");
        portletRequest.onreadystatechange = function() {.....}
        portletRequest.open("POST", "<portlet:resourceURL escapeXml='false' />");
        portletRequest.send("foo=" + bar);
    </script>


    Sample Application

    The XPRInvoiceAjaxPortlet sample showcases the usage of XMLPortletRequest. Using XMLPortletRequest drastically reduces the amount of code. Compare the view jsp page of InvoiceAjaxPortlet that does not use XMLPortletRequest with view jsp page of XPRInvoiceAjaxPortlet that uses XMLPortletRequest.


    Client Side Eventing

    Consider a JSR286 Java portlet which uses AJAX on client-side for actions. This typically calls the serveResouce method of the portlet which does not generate server side JSR286 events. Even if there is another JSR286 Java portlet, it can not leverage server-side eventing. Additionally, there may be other non-Java widgets on the page and all of these will work in silo in the absence of client-side eventing. On the other hand, if there is a framework to generate and propagate events on client, then this can benefit enormously. This feature tries to mimic JSR286 based eventing on the client.

    Consider a typical AJAX portlet which generates events. Although this portlet can be written in various ways, the example shown below tries to follow the pattern and conventions set by JSR286 spec for Java portlets.

    Assume there is a button that causes an action on the portlet. This will look something like below,

    <input name="getbtn" id="getbtn" value="Get Something" type="button" 
        onclick="<portlet:namespace>PortletObj.processAction('<portlet:resourceURL id="i1"/>')"/>


    Notice above that there is a Javascript object with name that has been namespaced using portlet tag to make it unique. Such a process action method for the JS object may be implemented as shown below,

    processAction : function(updateURL) { 
        portletReq = new XMLPortletRequest("<portlet:namespace/>");
        portletReq.onreadystatechange = function() {<portlet:namespace/>PortletObj.render();};
        portletReq.open("POST", updateURL);
        portletReq.send("formData=" + someData);
    }

    The callback is set to a render method on this portlet object as shown below,

    render : function() {
        // process response
        var div = document.getElementById("<portlet:namespace/>portletData");
        div.innerHTML = "";
        div.innerHTML = portletReq.responseText;
        var qName = { uri : "http:example.com/myevents" , name : "SomeEvent" };
        var values = {
            value : [{"foo" : bar}]
        };
        portletReq.setEvent(qName, values);
    }

    The most important thing to note is the event generation above. As per the JSR286 spec, setEvent could have been called in the processAction method. This would work if this was a simple case where some form data (which is ready at the time processAction is called), was to be passed on to the consumer of the event. If the event payload is more than the form data and if it is actually a result of data returned by the AJAX call to the portlet, then triggering the event needs to be postponed till this data becomes available. That means the event data will be available asynchronously when the callback is invoked. Or the event payload may even be returned as a JSON object or constructed as such. Hence the deviation from the JSR286 spec to set the event in render.

    The deployment descriptor for the portlet specifies the event as per the JSR286 spec. The only difference is the "value-type" which uses a dummy marker com.sun.portlet.ClientEvent.

    <portlet-app..........>
        <portlet>
            ...
            <supported-publishing-event>
                <qname xmlns:x=”http:example.com/events”>x:SomeEvent</qname>
            </supported-publishing-event>
            ...
        </portlet>
        ....
        <event-definition>
            <qname xmlns:x=”http:example.com/events”>x:SomeEvent</qname>
            <value-type>com.sun.portlet.ClientEvent</value-type>
        </event-definition>
    </portlet-app>

    This tells the portlet container that the portlet uses client-side eventing. The wiring of the portlets is handled by the portlet container in the exact same way as it would have been done for the server-side eventing(i.e based on the deployment descriptor information). This happens transparently to the developer.

    Thus, whenever the generator portlet calls setEvent, the event queue distributes the event object (payload) to all the portlets that support processing the event. This is also done in the deployment descriptor of the portlet as specified in JSR-286 spec and shown below.

    <portlet>
        ...
        <supported-processing-event>
            <qname xmlns:x=”http:example.com/events”>x:SomeEvent</qname>
        </supported-processing-event>
        ...
    </portlet>

    The event processing portlet has to implement a method processEvent(sample shown below). How the processEvent method is implemented is up to the portlet developer.

    processEvent : function (values) { 
        this.render(values.value[0].foo);
    }
      

    Sample Application

    The ClientSideEventing sample showcases the client side eventing feature.


    Download


    References

    Java Specification Request (JSR) 286
    OpenPortal Portlet Container 2.x
    Portal Post — a group blog
     
     
     
    Close
    loading
    Please Confirm
    Close