For comparison, we consider JSR168 primarily in the context of Java web server technologies, the core of which is J2EE and Java Servlets, but which also includes non-JSR168 Java portals.
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 replaces special markup in the response with HTML, and then a second filter which compresses 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 client requests pages from the portal webapp, and the portal invisibly delegates to portlet classes when it needs them to render.
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.
JSR168 leaves the method of communication between portlets to be defined in the next specification, so there is currently no official API through which portlets can exchange messages. 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 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 must 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.
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:
modify page layouts, e.g. add a new portlet to a page.
add or remove pages
manage users and roles
set page or portlet access permissions
find out what other pages there are on the site, and link to them
find out what other portlets exist on a page, or on the whole site
get a unique ID for its own "portlet window"
Management and inspection of pages, portlets and users/roles programmatically in portlet code can currently only be done using portal-specific extensions; normally, it is 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 the approach taken by portals in implementing these services, but this might become necessary if a standardised way for portlets to access the services were provided.
However, the remaining feature - knowing a portlet window's own ID, preferably one which persists across sessions - is a much simpler one which can be critical when developing portlets as communicating components. This is particularly relevant when there are 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:
set a PORTLET_SCOPE attribute with a known name, e.g. "retrieveID". To ensure that there will be no interference from other portlets doing the same, randomly generate part of this name.
get a list of the attributes using PortletSession.getAttributeNames(APPLICATION_SCOPE)
find the full (namespaced) name of the attribute that was just set, by iterating through the list and using PortletSessionUtil to find the one containing the PORTLET_SCOPE name
break down this full name into parts as described in PLT.15.3 to extract the Window ID: "javax.portlet.p.<ID>?<ATTRIBUTE_NAME>"
store this portlet window ID for later retrieval (e.g. in a PORTLET_SCOPE session attribute 'windowID')
clean up: remove the PORTLET_SCOPE attribute that was set earlier
Sometimes it is useful for portlets to know the ID of the user's
whole portal session, not just the IDs of the individual portlet
application sessions (which can be retrieved with PortletSession.getId()).
This is particularly so when portlets in different applications need to
communicate. The ideal identifier is simply the Portal application's
own session ID - but this session, and its ID, are not directly
accessible to portlets. The JSR168 method PortletRequest.getRequestedSessionId() can almost provide equivalent functionality, as it returns the session ID included in the client request. However, unlike HttpSession.getId() or PortletSession.getId(),
this approach does not work with newly-created sessions - i.e. the very
first page render. This can be inconvenient if as part of session
creation the portlet needs to set up external resources reliant on the
portal session id (like external message stores).
Workaround Apart from using PortletRequest.getRequestedSessionId()
(and having to account for its absence on the first page visited), two
approaches have been investigated: setting a session ID in a cookie
visible to all portlets (which also has the first-page problem, and is
additionally quite unreliable), or modifying open source portals to
pass on the Portal's session ID in the PortletRequest. None of these are ideal, but we have not found any other solutions so far.
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 does support some headers (such as Content-Length and Content-Type) with 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.
Portlets generate only a fragment of the eventual response sent to the client. Therefore, if a portlet needs 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.)
To provide a file for download, a portlet must cause the client to make a new request - either directly to the file, or to a servlet which retrieves it. This is done by either rendering 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 generates 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).
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.
Many portlet programmers have encountered issues which are not missing features, but areas of possible confusion often due to differences between portal implementations.
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 position 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; a content management portlet would be set up to display different content on different pages.
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:
Can there be different Portlet Windows of the same portlet which are associated with different preferences objects?
- on the same page?
- on different pages?
Do different Portlet Windows also maintain associated, separate PORTLET_SCOPE Sessions and render states (from Action requests) as well as preferences?
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 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 in the background, 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, either the necessary state must be included with every AJAX request (as GET parameters, so only a small amount of data can be sent this way), or the servlet would only be able to perform tasks that are completely independent of the portlet state.
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 (Liferay, Jetspeed 2) 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. This is not an issue with the portlet specification, but more generally with J2EE applications.
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.
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 (e.g. commons-fileupload, which now supports portlets in version 1.1 released Dec 2005).
Workaround 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.
Inter-portlet Communication and Portlet Filters are already intended to be part of the next specification.
In addition, we would like to see the following features considered:
Cookies: be able to read them, and set them in Action phase
Clarify support for Portlet Windows (instances of the 'same' portlet on different pages)
Retrieve ID of current Portlet Window
Retrieve user's Portal Session ID (the same for all portlet applications, and even when session is new)
Retrieve the remote (client) IP from the request
Some form of 'Exclusive' display mode: this would allow portlets to serve non-HTML content
Access to Portal Services
inspect/manage page layouts, pages
inspect/manage users and roles
Allow portlets to be queried by AJAX requests. This would need some form of an 'Exclusive' mode and an actual endpoint on the portal allowing direct requests to a portlet window.