ShowTable of Contents
If you have been developing portlets since the early days of the IBM Portlet API you remember that you could always take advantage of the query parameters on the incoming Portal URL. This all changed, however, when the Portlet 1.0 specification (JSR 168) came out.
For security reasons the specification required that the portlet container hide all the query parameters from the portlets, unless the request was an action or a render request targeted at a specific portlet. Therefore no query parameters on any generic request to the page could be seen by any other portlets.
The specification also stated that render parameters must be set by the performAction method and that the portlet container may only pass those render parameters to the portlet's render method.
Many portlet developers believed that the Portlet 2.0 specification (JSR 286) allowed access to query parameters again with the definition of Public Render Parameters. I, too, assumed that all you would need to do is identify the render parameter in the portlet deployment descriptor, like the specification says, and when that parameter appeared on the URL for a page, it would be made available to the portlet's render method.
Unfortunately, this is not the case. If you read the specification a little more carefully, the Public Render Parameters are intended only to share render parameters between other JSR-286 portlets. The query parameters are still not available to portlets.
Since the Portlet 1.0 specification came out, there have been many solutions presented to overcome this limitation. The developerWorks® WebSphere article, “Retrieving URL parameters from JSR 168 portlets using WebSphere services
,” gives one example, in which a servlet filter is used to put the parameters into a global cache so any portlet can retrieve them. As the article points out, the implementation of the filter puts all parameters into the cache.
Since the cache is global, the parameters must be namespaced with the session ID and the cache could fill up, dropping parameters out of the cache before the end of the associated request. Even if you were selective about the parameters you placed in the cache, there is still the remote possibility that two requests from the same session could arrive in parallel.
The “well known cache” solution has been used by others to solve some portlet-to-portlet communication options also. However, with the PropertyBroker interface and the other advancements in portlet-to-portlet communication, I view this as no longer necessary.
The new System Programming Interfaces (SPIs)
My plan to solve this problem was to use the new SPIs available since WebSphere Portal version 6.0. These SPIs are described in the WebSphere Portal 6.0 Information Center and in the developerWorks article, “Leveraging WebSphere Portal V6 programming model: Part 2. Advanced URL generation in themes and portlets
My first attempt at a solution was to put some logic in a servlet filter. Using the filter, I thought I could retrieve the current state of the portal, change the state to point to the target portlet, add the parameters for that portlet, and then respond with a redirect.
I found, however, that when the servlet filter executed, the portal state in the original request had not yet been processed, so the current portal state was not available. The other drawback of this solution was that the servlet filter was more difficult to install and added logic that would be executed on every incoming portal request.
The next attempt was to package the logic in a separate servlet, whereby a static URL could be sent to the servlet with the appropriate parameters. The servlet could use the Navigational State SPI to build a portal URL to the target portlet on the target page, with the requested parameters, and then redirect the user there.
This implementation worked fine for the render requests, but the drawbacks were:
- since the incoming URL had no previous portal state, the generated URL reset the portal back to an initial state
- when an action request was generated, the portal rejected it because it did not have an action ID. (I explain action IDs later in this article for those readers unfamiliar with them.)
The third iteration was to create a custom theme, with the objective of using the PortalNavigation:URLGeneration tag that is available to all themes. This turned out to be much simpler to implement than the two previous iterations because the tag gave me a simple interface to specify my intentions, and the tag implementation did all the complicated work with the Navigational State SPI.
This solution allowed me to handle render and action requests, both HTTP and HTTPS requests and requests to virtual portals. The action requests were valid because I was using the theme tags, which automatically generated a valid action ID; however, this also turned out to be the fatal flaw. (I refer to the explanation of the portal action IDs later.)
An existing JSR 286 solution
After successfully implementing my solution with the custom theme, I found two additional resources that were helpful.
The first is the article, “Comment lines: Sending parameters to the JSR 286-based Web Content Viewer portlet from external applications,"
which describes how the new IBM Web Content Manager (WCM) rendering portlet uses the Uniform Resource Identifier (URI) Resolver Framework to enable parameters to be passed to it. With this capability, you can create a human-readable URL to reference any specific content item.
At first I assumed that the URI Resolver Framework was not public because I could not find any references to it in the Portal Information Center or the Portal Javadoc, but then I found the article, “Leveraging WebSphere Portal V6 programming model: Part 5. Accessing portal content with custom URLs.
Published in 2007, that article described the URI Resolver Framework as a public SPI and also supplied an example. The example offered a lot of functionality, enabling you to target a portal page, specify the window state for multiple portlets on that page, and specify render parameters for one or more of those portlets.
This configuration can then be referenced by creating a fairly simple URL that specifies the scheme and the name of the link:
The example was also restrictive, however, in that it allowed you only to reference a link defined in the configuration file, and it only allowed you to send a render request. My original requirements were to be able to send query parameters on the URL itself, and I expected to be able to send both render and action requests.
Now that I had found a public extension point in the WebSphere Portal product, I knew that I must use that, instead of creating my own back door. I decided to simply extend the existing solution and, if nothing else, make the world more aware of this extension point.
My example simply extends the capabilities of the XML configuration file and lets you specify parameters at the end of the incoming URL. I also defined a new scheme named deeplink2 to ensure it would not collide with the original example, meaning that the composed URL would like this:
Or a protected URL would look like this:
The solution involves two elements:
- A URI resolver that must be registered and must be able to resolve a given URI to a valid Portal URL. The resolver is registered under a specific scheme name.
- A configuration file that defines the valid targets. In the configuration file these are called deeplinks, each of which defines a portal page, portlets, and parameters to be used.
The previously mentioned article (Leveraging WebSphere Portal V6 programming model: Part 5. Accessing portal content with custom URLs
) provides an excellent explanation of the URI Resolver Framework and how to implement your own resolver, so I will not repeat that here.
I will state that the framework seems to be designed so that individual portlet developers could implement their own resolver and register their own scheme, to allow users to create static references (URIs) to content that their portlet displays.
This is what the JSR 286 WCM Rendering Portlet did, but it seems that third-party developers have not yet caught on to this capability. Moreover, the missing link is the knowledge of what page the portlet happens to be on.
For now we must live with a generic implementation that lets administrators define deeplinks with parameters in a global configuration file.
In addition, part two of the same article series (Comment lines: Sending parameters to the JSR 286-based Web Content Viewer portlet from external applications
) gives a very good description of the Navigational State SPI and how to use it, which I also don't repeat here.
What I needed to do was to extend this solution in two places, allowing:
- parameters to be supplied on the incoming URL that can override parameters specified in the configuration file, and
- a portlet action to be targeted, instead of only allowing the parameters to be supplied to the render method.
Overriding render parameters
For this functionality I decided to only allow parameters that are already specified in the deeplink definition (in the configuration file) to be overridden. I believed this to be much more secure than simply passing all the query parameters from the incoming request.
This approach allows different parameters to be passed to different portlets and, at the same time, restricts the parameters that can be sent to a portlet. This prevents someone from adding a bunch of parameters to a URL and somehow overriding a value that was never meant to be overridden.
To implement this, the configuration file XSD was changed to allow an additional attribute on the parameter tag. The additional attribute is called “allow-override” and has the value of true or false, with false being the default value.
You can see an example of the attribute in the definition of link2 in the example configuration file, as shown in listing 1.
Listing 1. Example of allow-override attribute
<deeplink linkname="link2" page-uniquename="deeplink.page">
<parameter name="origin" allow-override="true">origin2</parameter>
<parameter name="destination" allow-override="true">destination2</parameter>
The other change was in the DeeplinkResolver code. If allow-override is true and the parameter exists on the incoming URL, then the value from the URL is passed to the portlet; otherwise, the value in the configuration file is used.
Invoking a portlet action
This added functionality needs a bit of explanation because it can introduce a security risk having to do with invoking a portlet action arbitrarily. If we allowed this, as many Web applications do, two things would be possible:
- The user could accidentally click a button twice or cause the action URL to be invoked in some other way during their session.
- A malicious person could construct an action URL, like a purchase request, for a commonly used Web site and automatically invoke that URL on their own Web page. If you happened to be logged into that commonly used Web site when you landed on this malicious Web page, then the action would actually be executed successfully.
In this way the third party has secretly hijacked your identity on the commonly used Web site just because you used their Web page in the same browser session.
To prevent these situations, WebSphere Portal always adds an action ID to any action URL. Action IDs are scoped to a session and, when an action request is received, the action ID is checked to ensure that it is valid. Invalid or “previous” action IDs are not allowed.
This protects the portal from the above two situations because:
- if the user accidentally caused the same URL to be invoked twice, then the action ID would be the same both times and would be rejected by the portal, and
- if a malicious person constructed an action URL, it would either not have an action ID, or the action ID would not be valid when used in the context of another user's session.
This assumes that an action actually does something; in other words, has a side effect. The best practice for any portlet is that a render method should never have a side effect and therefore can be executed any number of times. An action request therefore is assumed to have a side effect, which is why it is protected by the above logic.
So, when defining the deeplinks in the configuration file you should be aware of whether the URL has a side effect. This is true regardless of whether the URL is a render request or an action request, because you cannot depend on all portlet developers following the above best practice.
To implement this capability, the configuration file XSD was changed to allow an additional attribute on the portlet tag. The additional attribute is called “action” and has the value of true or false, with false being the default.
Virtual portals are also supported simply by addition of the virtual portal URL context after the “poc” or “mypoc.” For example, if you have a virtual portal with a URL context of “vportal1”, then the following URL could be used:
Since both pages and their unique names are scoped to a virtual portal, you can actually use the same unique name for the page in both the base portal and the virtual portal. This lets you use the same link in the configuration file to access a page in either one.
Advantages of the resolver framework solution
As explained above, each of the previous solution designs had their own problems that limited their functionality. The URI Resolver Framework approach has none of those restrictions and seems to cover all the requirements, in that it:
- Uses a public extension point in the Portal product
- Allows a request to be targeted to a specific page and/or portlet
- Can send parameters to one or more portlets on the page
- Can send render request (render method) or action request (performAction method)
- Has a dedicated URL; no need to have a servlet filter that must inspect every single incoming URL
- Has a small URL; the parameters can be hidden in the configuration file or overridden in the URL itself
- Can append uri parameter (uri=deeplink2:link1) onto a normal, generated URL with portal state
- Works with protected (mypoc) and non-protected (poc) URLs
- Works with HTTP or HTTPS
- Works with virtual portals
- Is deployed in its own EAR; no need to update the wps.ear file
Disadvantage and inadequacies
The one disadvantage of this solution is that you can loose the portal state that is encoded in the portal URL any time you use a plain, human-readable, friendly URL, a URL mapping, or a POC URL. This is because these URLs are static and are usually written down and passed to external applications, not generated on the fly with the current state of the portal for the current user.
This is not a big problem, though. Since these URLs are usually given to external applications, they are normally used as an entry point into the portal, so there is usually no state to worry about. Also, if you generate the URL on the fly, you can append the POC URI to an existing stateful URL, thereby preserving the current portal state.
Improvements required for production use
This implementation also has its inadequacies. First, it uses an XML configuration file to define the possible target portlets. This should obviously be connected to a database to be truly useful in a production system. But, for the purposes of this article, I didn't want to make it more complicated to install.
The second improvement necessary to make it production-ready would be to provide a user interface to manage the deeplink definitions. The current deeplink administration portlet merely lets the admin reload the links and lists the currently defined links.
Installing the deeplink2 example
Installing the deeplink2 example is easy. All the files you need are packaged in the attached ZIP file, along with a file containing example URLs to test the example page and portlet. Just follow these steps:
- Download the .zip file and extract its contents into a temporary directory.
- Install the Deeplink2ResolverEAR with the WAS administration console (be sure to specify the WebSphere_Portal server or cluster).
- Save the configuration and start the new Enterprise Application (DeeplinkResolver2EAR).
- Be sure the url tag in DeployDeeplinkPortlets.xml points to the expanded DeepLinkResolver2.war directory, and deploy the Deeplink2 portlets, using the XML file.
- Create the Deeplink2 example page using the CreateDeeplinkPages.xml file.
NOTE: Steps 4 and 5 can be done via the Import XML menu in the Portal Administration pages.
The above five steps install the Deeplink2 resolver, the example portlets, and the example portal page. The resolver itself is automatically registered via the plugin.xml file in the WEB-INF directory of the WAR.
If you choose, you can change the application name, context root, or unique names during the install, but be sure to change the corresponding references in the XML files.
This article has described an architecturally sound solution to the challenge of passing parameters to a JSR-286 portlet from outside the portal JVM, taking advantage of capabilities already in the WebSphere Portal product.
The solution presented is generic enough to support many different portlets at the same time and can be used by clients both inside and outside of the portal. There is also a measure of safety in the implementation because the solution allows you only to send requests to certain portlets and also specifies which parameters can be overridden.
Retrieving URL parameters from JSR 168 portlets using WebSphere services:
Leveraging WebSphere Portal V6 programming model: Part 2. Advanced URL generation in themes and portlets:
Leveraging WebSphere Portal V6 programming model: Part 5. Accessing portal content with custom URLs
Portal 6.0 Advanced URL Generation Helper classes
About the author
John De Binder joined IBM's Federal Systems Division in 1979 and worked on the Space Shuttle launch system for five years. Since joining IBM's Software Group in 1984, he has worked on a Graphics Design product, parts of the OS/2 operating system, and the IBM Distributed Smalltalk product.
In 1996 he was one of a small group that started the SWG Services Consulting team, first on IBM Smalltalk, then with WebSphere Application Server, Java, and WebSphere Portal with IBM Software Services for Lotus® (ISSL). John is now on the WebSphere Portal development team working on special projects where he still helps clients. He can be reached at email@example.com
The author thanks Carsten Leue for reviewing this article and providing his insights into the resolver API, and into Web and URL best practices.