Draft Portlets Chapter (partial)

Limitations in Portlet Implementation

Missing Portal Features

The JSR168 specification (PLT.E) notes some features as missing, and to be considered in a later specification:

These features and others are discussed in this section.

Portlet Filters

The Servlet API includes support for Filters (SRV.6), which wrap the processing of an incoming request. A filter can inspect the incoming request and modify the response returned to the client, and can therefore be used for a multitude of tasks from access-checking to transformation of the response content.

Filters are configured in the webapp's web.xml configuration file, and can be mapped to individual servlets or particular URL patterns covering many servlets and/or other files. It is then the servlet container's responsibility to manage and apply the filters to any incoming request. Multiple filters can be applied to a single request - for example, a request might first be passed through a filter which replaced special markup in the response with HTML, and then a second filter which compressed the response before transmission.

Servlet filters deployed in a portlet application are not applied to portlets in that application. Filters are only applied to resources in that webapp which are served by the servlet container to a client; this never happens with portlets. Instead, the portlet classes are used directly by the Portal to generate their display.

JSR168 mentions portlet filters as a feature which will be defined in the next specification. The functionality provided by filters can currently be duplicated by other methods, such as having portlets inherit from a parent class with the required common features, or the workaround below.

Workaround A generic, JSR168-compliant, Filter Portlet has been implemented by Bridges project (currently part of Apache Jetspeed 2.0), which can provide similar functionality to servlet filters. This is done by registering the Filter portlet in the portlet.xml, and parameterising it with the name of the 'real' portlet class. The Filter portlet then acts as a wrapper around the real portlet.

Another option is to use aspect-oriented programming to intercept portlet methods and wrap them, which would be very similar to filters but differ in the method of configuration.

Portlet applications using either 'filter' replacement should be relatively easy to modify to support real portlet filters when it is time to upgrade to the next standard.

Inter-portlet Communication

JSR168 leaves the method of communication between portlets to the next specification, so there is currently no official API through which portlets can send messages to each other. Communication is still possible, by using shared session attributes visible to all portlets in the same application, or by using external message stores such as databases. However the detailed implementation of such communication is left up to the portlet developer.

Conversely, many Portals make their own detailed provisions for inter-portlet communication, using a variety of models and configuration methods, and have done so since before JSR168 was published. The use of such communication services saves considerable development time, but the portlets developed are then of course portal-specific.

Workaround As a stopgap measure before the next portlet specification is finalised and implemented by portals, we have implemented a library for portlet messaging which works within the limitations of JSR168. This enables developers to write communicating portlets easily without needing to implement the messaging system as well, thus partially levelling the field between JSR168 and portal-specific messaging. Of course, an arbitrary third-party portlet will still not be able to participate in such communications without being rewritten to use this messaging library - but that level of compatibility will have to wait for an official inter-portlet communication API in the next specification.

A more detailed description of existing portlet communication systems and the design of the messaging library is given in the next chapter.

Access to Portal Services

Services such as management of pages, portlets, users, roles and access permissions are provided by the Portal hosting the portlets. With JSR168, there is no way for a portlet to access these services: it cannot inspect its page environment or other portlets, and security is limited to the PortletRequest functions getUserPrincipal/getRemoteUser and isUserInRole.

As a result there are a number of things a JSR168 portlet is unable to do, including:

Management and inspection of pages, portlets and users/roles in portlet code can currently only be done using portal-specific extensions; otherwise it must be done using the portal's own management tools. While many of these functions would be very useful in some scenarios, a question would remain over how much power a portlet should be given to actually modify its own hosting environment (read-only inspection would be less of a risk, but still perhaps a security concern). Even if access to such functions were to be provided in a later portlet specification, it would be likely that final control would remain with the portal administrator, who would be able to configure the permissions granted to portlets. In addition, different portals currently use different models for managing pages and users; it may not be appropriate or desirable for the portlet specification to specify in detail approach taken by portals in implementing these services, as might become necessary if a standardised way for portlets to access the services were provided.

On the other hand, knowing a portlet windows's own ID is a much simpler requirement which can be critical when developing portlets as communicating components - especially when it is likely that there will be multiple windows showing the same portlet (the concept of portlet windows is discussed later in this chapter). The Portal itself will have assigned an internal ID for each portlet window, to be able to store and retrieve portlet preferences etc., but JSR168 provides no simple way to access this ID from within the portlet. The method RenderResponse.getNamespace() may be suitable in some situations, but only provides a namespace for the portlet window which is unique for the current page (PLT.12.3.4) - it is not guaranteed to be unique across the whole site, or even consistent across different requests in the same session.

Workaround It is possible to generate a unique ID for a portlet window, and store it in its local session when the window is first rendered; this is quite easy to implement. A per-session ID can be guaranteed to be unique for the portlet application by registering and checking it against a list of assigned IDs in the APPLICATION_SCOPE session. If the ID must persist across multiple sessions, it can instead be generated once and stored in the user's preferences for that portlet window - however, this approach opens up the possibility of ID clashes with portlets which have not yet been initialised in that session, and so an official method of retrieving the ID would be preferable.

Another, more reliable, workaround might be to make use of the portlet window ID which is used by the portal to namespace PORTLET_SCOPE session attributes, as described in PLT.15.3. Assuming that the portlet is able to inspect the APPLICATION_SCOPE session and access the full name of namespaced session attributes, it would be able to retrieve the portal's unique ID for the portlet window as follows:

Cookies

Cookies are (usually) small pieces of information stored on the client by the browser as name-value pairs, used for client-side tracking of state. They can be set either by a response header from the server, or using JavaScript. When making a request to a site, the browser includes any cookies set by that site as a header in the request.

Reading Cookies

Servlets can access client cookies using the method HttpServletRequest.getCookies(). There is no direct equivalent for this in the PortletRequest, however on some Portals the cookie Header value can be accessed using PortletRequest.getProperty("cookie"). This approach is not guaranteed to work reliably on all portals. According to PLT.11.1.4, this is due to differences in portals and servlet containers which may make the headers unavailable to portlets. JSR168 makes provision for this for some headers (such as content-length and content-type) by adding specific access methods in the PortletRequest interface, but unfortunately this was not done for cookies.

Hopefully the retrieval of cookies will be addressed in a later specification.

Workaround Some portals will allow the cookie to be retrieved using PortletRequest.getProperty("cookie"), but this cannot be relied upon.

Setting Cookies

Servlets can set cookies on the HttpServletResponse using either addCookie or setHeader. Neither is available to portlets on the PortletResponse or its subclasses.

A portlet cannot be expected to be able to set response headers during the render phase, as it can only affect its rendered page fragment. However, it should be theoretically possible for a portlet to set a cookie on an ActionResponse in the action phase (which occurs before any rendering takes place), as it is already possible to send a redirect header during this phase.

Hopefully a later specification will permit cookies to be set on an ActionResponse.

Workaround Without support in JSR168 for setting cookies as part of the response, it is necessary to resort to approaches which are more clumsy and fragile. One option is to set a cookie using JavaScript, which can be done by having the portlet generate the JavaScript as part of its rendered display - however this relies on JavaScript being enabled on the client. Another option is to add a servlet to the portlet application which sets a cookie when it is accessed. This servlet must be accessed directly by the client, not dispatched to through the portal, and so it must be loaded through an IFRAME or a popup window generated as part of the portlet display. Both approaches (JavaScript & servlet) have the disadvantage that the cookie is set only as a result of the client viewing an already-rendered portal page, so the cookie will only be available to portlets on subsequent requests.

Session ID

A portal may contain portlets from different portlet applications. If any sort of communication between portlets in different portlet applications is to take place, each portlet must be able to identify the user session it is participating in. Currently, communication between portlets in different applications is discouraged. However it is possible - given a suitable globally-accessible message store, and access to an identifier for the whole portal user session (not just each portlet application's user session). Further details of such cross-context communication are discussed in the next chapter.

We have been unable to find a suitable 'Portal session' identifier accessible to portlets through JSR168 interfaces - although an ideal identifier exists: the Portal's own session ID.

Workaround A satisfactory workaround has not yet been found, but two approaches have been investigated: setting a session ID in a cookie visible to all portlets, or modifying open source portals to pass on the Portal's session ID in the PortletRequest. Both are discussed in more detail in the next chapter.

'Exclusive' display mode

Portlets generate only a fragment of the eventual response sent to the client. Therefore, if a portlet wishes to serve binary content (images, PDFs etc) to a client, it cannot directly include these into the HTML stream. (Some browsers permit binary objects to be embedded in HTML, but support for this is not yet widespread.)

If a portlet wishes to provide a file to a client for download, it must therefore do so by delegating the task to a servlet: either providing a link to it, including it in an IFRAME, or opening it in a new window. However, problems related to the treatment of cross-context sessions may be encountered when implementing these approaches on some Portals (discussed below).

As an alternative, some Portals have chosen to make available an 'Exclusive' mode in which a portlet is effectively 'full-screened' and can generate the entire response, not just a fragment. This approach is portal-specific but quite popular, and may also have relevance when considering the use of AJAX in portlets (discussed below).

Remote (client) IP

Servlets can find out the IP address of the client which sent the request, through ServletRequest.getRemoteAddr() or getRemoteHost(). This can be useful for limiting access by IP address or logging IPs associated with actions for admin or security purposes.

However, the PortletRequest classes do not provide an equivalent of these methods, and it is therefore not possible for a portlet to directly retrieve the client's IP address from the request as a servlet can. Nor can this be worked around by causing the portlet to dispatch to a servlet - JSR168 specifically prohibits the ServletRequest methods concerned from working in this context (PLT.16.3.3).

Workaround We have not found a good workaround for this issue. One possibility is to use a combination of servlets and cookies as described earlier, which would require support for reading cookies in portlets, an additional servlet in the portlet application, and at least one page render before the portlets would have access to the client IP.

Further Problematic areas

Portlet Windows/Instances

Portlets are defined with entries in the portlet.xml deployment descriptor. PLT.5.1 specifies that only one actual instance of each portlet so defined will be created by the portlet container (or one per VM in the case of a distributed application). This single instance will be used to service any Action or Render requests targeted to that portlet.

However, the same portlet - with only one definition in the portlet.xml - can be placed in more than one place on a site, and often it is necessary for these different portlet entities to operate independently. For example, a 'Stocks' portlet might be configured to show a report for different stocks depending on the page; a portlet which could be pointed at any WSDL file to generate an interface to a web service might be present on different pages, providing access to web services appropriate to the page.

JSR168 defines a 'Portlet Window' as the combination of a portlet and its preferences on a portal page. It also states, "A portal page may contain more than one portlet window that references the same portlet and preferences-object." (PLT.5.2.3). The details of creating and managing portlet windows are left to the Portal and Portlet Container.

This leaves several aspects of the concept of a 'Portlet Window' undefined or ambiguous, including:

The answer to the latter question is usually assumed to be 'yes', but the first is more variable. Different portlet containers and portals are implemented using different assumptions.

The strictest approach is to assume a simple mapping of portlet definition (in the portlet.xml) to portlet window: adding a second portlet window for the same portlet will merely result in a mirroring of content from the first. In this case, multiple definitions in the portlet.xml for the 'same' portlet (merely differing in portlet name) must be added if multiple independent portlet windows are required.

The most flexible approach is to treat every portlet window on a page as an independent entity, with its own set of preferences, PORTLET_SCOPE session, and render state. This allows for an unlimited number of portlet windows associated with a single portlet definition, and is particularly well-suited to sites which give users the freedom to add portlets to their pages. This is the approach taken by Apache Jetspeed and Oracle Portal.

Intermediate approaches are also possible. For example, a portal might allow portlet windows from the same portlet definition to exist independently on different pages, but not on the same page.

Differences in portal behaviour on this subject do not significantly affect the process of developing portlets. They do affect the way the portlet deployment descriptor is written, and the way site designers and users add portlets to pages. For dynamic sites which allow users to create pages, the strict intepretation may not be sufficiently flexible, depending on the expected level of portlet reuse.

The PortletSession and the HttpSession: Cross-context handling

According to JSR168 (PLT.15.4):

"The PortletSession must store all attributes in the HttpSession of the portlet application. A direct consequence of this is that data stored in the HttpSession by servlets or JSPs is accessible to portlets through the PortletSession in the portlet application scope. Conversely, data stored by portlets in the PortletSession in the portlet application scope is accessible to servlets and JSPs through the HttpSession."

This behaviour is also described in PLT.3.1: Bridging from Portlets to Servlets/JSPs.

This clearly allows for portlets and servlets in the same portlet application to easily work together, communicating through the shared session. The session may be used to send data (for example if the portlet wishes to delegate to a servlet to generate a binary file), or details of the logged-in user (which the servlet may use to confirm that the request has been suitably authenticated).

However, there are implementation issues which mean that this behaviour is not found in some servlet container/portal setups. In such cases, a servlet which is accessed directly (e.g. one displayed in an IFRAME in a portlet, or in a new window) will not see the contents of the PortletSession, but instead its own separate HttpSession. This HttpSession is in fact the session associated with the portlet's (and the servlet's) webapp, whereas the PortletSession is associated with the Portal webapp, and these are kept separate following the Servlet 2.3 specification (SRV.7.3). Then, the only way the portlet can send information to the servlet is by appending URL query parameters, which may not be appropriate or possible for large amounts of data. If on the other hand the servlet is included in a portlet's output through the portal, using (in a portlet render method) getPortletContext().getRequestDispatcher(jspPath).include(request, response), or (in a JSP included by the portlet) jsp:include, the session behaviour is correct as described in JSR168.

From testing with Jetspeed/Pluto on Tomcat 5.0 and the default configuration of 5.5, servlets accessed directly do not share the portlet session. In Tomcat 5.5 configured with emptySessionPath="true", behaviour is correct as described in the portlet specification.

AJAX in portlets

AJAX has become increasingly popular in developing dynamic web interfaces which can change their display and retrieve information from the server without requiring the user to submit a new request or reload the page.

The underlying technology uses JavaScript to asynchronously fetch a web page, reading in and using the returned data to modify the page seen by the user. This requires an endpoint on the server - usually a dynamically-generated page which takes query parameters - for the JavaScript to fetch. In the context of Java web servers, this endpoint would be a servlet or a JSP.

Portlets may wish to use AJAX in their interfaces - particularly considering the additional overhead involved in reloading a portal page - and can deploy the corresponding endpoint servlets (or JSPs) in their portlet application.

One potential problem with this approach is due to the previously-described cross-context sessions issue: if the servlet does not share the same session as seen by the portlet, AJAX calls will be unable to see or update the portlet session state. In this situation, the servlet would only be able to perform tasks that are completely independent of the portlet.

Another potential problem - assuming that the servlet and portlet do see the same session - is that the servlet will only be able to see and set attributes in the APPLICATION_SCOPE portlet session. Thus AJAX calls to a servlet will not be able to modify session attributes belonging to that portlet window, or indeed access any portlet-specific functionality such as initialisation parameters or preferences.

Thus AJAX calls which access portlet functionality (e.g. PORTLET_SCOPE session, initialisation parameters, preferences) are not currently practical using JSR168. To enable this, the portal would need to provide an endpoint for a portlet window which would act in a similar way to the 'Exclusive' mode described earlier: the portlet, in a special render mode, would be able to output the entire page. Then, an AJAX call would not need to access a servlet, but instead could query the portlet directly. However, making the 'Exclusive' render mode simply a new Portlet Mode would not have exactly the required effect: the mode of the portlet firstly can only be changed in an Action phase, and secondly should not have been changed as a result of AJAX requests when the user later revists the portal page. Instead, this usage pattern might require either the ability to specify a portlet mode change for a single request, or a new portlet request type alongside 'render' and 'action'.

Authentication Integration: JAAS

Each J2EE application server has a different way of configuring new JAAS login modules, so installation of the portlets which make use of JAAS to access remote resources will additionally require this administrative task.

We found that support for JAAS in different portal/application server combinations was sometimes obscure or unreliable, and workarounds or special configurations sometimes had to be added. In particular we found that several portals deliberately override the JAAS settings of the host J2EE server (e.g. Tomcat). Integration of an existing authentication mechanism with that of a particular Portal may therefore be an initial time-consuming task.

Library conflicts

In some cases, the variety of libraries available from the servlet container and the portal can result in version conflicts with libraries included by the portlet application. This is particularly noticeable with logging libraries and Struts.

Conflicts with commons-logging or log4j libraries are usually resolved by removing copies of the libraries from the portlet application, and simply using the ones present in the server or the portal webapp. Some portlet application deployment processes (e.g. Jetspeed's) will automatically remove such logging libraries.

The issue with Apache Struts is more complicated. Struts is commonly used as the framework for servlet applications using the Model-View-Controller (MVC) design paradigm, but is not yet directly compatible with the portlet model - later versions should provide transparent portlets support. Until then, workarounds are necessary. One of these is a generic Struts bridge, developed as part of the Jetspeed 2 project, which allows an existing Struts-based application to be used as a portlet, and can also be used with other portals. However some portals instead provide their own methods of using Struts with portlets, e.g. with a modified Struts library, and/or inheritance from a StrutsPortlet class provided by the particular portal. This custom Struts support can interfere with more standard use of Struts for servlets in portlet applications - e.g. in helper servlets that should work independently of the portal. In some cases, when the portlet application is deployed, the portal will modify or replace the Struts configuration or the Struts library used, so that the 'normal' Struts-based servlets may no longer work. Such conflicts may be much harder to resolve.

Using common code for portlets and servlets

Portlets and servlets use similar, but different classes for representing HTTP Servlet requests, responses, and sessions. This makes sharing common control code between servlets and portlets difficult: for example, a utility function which would get or set a session variable would not be usable in both portlets and servlets, despite the function calls being very similar in appearance (e.g. HttpSession.getAttribute(name) v.s. PortletSession.getAttribute(name)). In addition, the problem may arise when using common libraries which do not (yet) provide a portlet version.

If this issue does not appear in many places, and the code can be modified, it may be acceptable to add a few duplicate functions that simply take different parameter types - however this is bad coding practice. Portal-specific solutions are sometimes suggested to obtain an object of the required class, using special knowledge of how to retrieve a particular object from a request, or by knowing which objects are safe to cast (e.g. Apache Pluto's portlet RenderRequest can be safely cast to a HttpServletRequest).

Our generic, JSR168-compliant solution was to write a number of simple wrappers or adapters, so that a Portlet object can masquerade as an HTTP Servlet object (or vice versa). For our requirements - mostly session access - this was sufficient, although this approach will clearly fail if an attempt is made to call a function which is not available on the underlying object. The only significant consideration when developing and using these wrappers was that the different scopes available in a PortletSession must be accounted for: when wrapping Portlet and Http sessions, it is necessary to specify whether the underlying session should be treated as a PORTLET_SCOPE or APPLICATION_SCOPE session.

----- Revision r1.6 - 07 Dec 2005 - 18:04 GMT - Main.MichelleOsmond
Copyright © 1999-2005 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.