torsdag den 7. februar 2008

Making the BEA Weblogic Portal bookmarkable

The issue


The two big Portal servers I know have problems with state. WebSphere and WebLogic.

It seems as though somewhere along the productline, someone concluded that maintaining state in the portal while having clean bookmarkable URLs and back/forth working, was too much of a hassle, so they decided to disregard bookmarkability and browser history.

In any major portal project I have worked, this issue has come up. The customer says it's a 'need to have', and I agree.. but the portals really should handle this internally.. alas they don't.

A Weblogic solution


Through an experienced BEA developer I heard of a URL-rewriter plugin for the WebLogic portal, which might offer a solution. The idea is pretty straightforward, anywhere a URL is compiled, the URL rewriter is called upon to do it's *magic*.
Decompiled from 'com.bea.portlet.GenericURL', it looks like this:

if(urlRewriterPlugin != null)
{
ServletContext servletContext = UIContext.getFromRequest(httpRequest).getServletContext();
String target = path;
if(path != null || contextualPath != null)
{
DefaultUrl defaultUrl = resolveTemplate(removeUnsetParams);
target = defaultUrl.url;
}
if(scopedRequest != null)
val = urlRewriterPlugin.createUrl(target, params, getUrlType(), isStandalone(), scopedRequest, httpResponse, servletContext);
else
val = urlRewriterPlugin.createUrl(target, params, getUrlType(), isStandalone(), httpRequest, httpResponse, servletContext);
if(debug.ON)
debug.out((new StringBuilder()).append("Output from the URL rewriter: ").append(val).toString());
if(urlCompression != null && !suppressUrlCompression && val != null && !val.startsWith("wsrp_rewrite?"))
val = compress(val);
}


As you can see its a typical adapter pattern. What we can also see is that the URL rewriter also has to address the complexity of ScopedRequest, which is probably used for referencing any resources inside the scope of a portlet.

Implementing the rewriter


Firstly it must implement the interface 'com.bea.portlet.GenericURL.UrlRewriterPlugin'.
this interface forces the method:

public String createUrl(String path, Map parameters, String urlType, boolean
isStandAlone, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext);

Now, there's a lot of different parametres here, but we are only/mostly interested in parameters and the request.
Back to the issue of the ScopedRequest; We actually just want to ignore it, any request string coming in, should be the same coming out with the parameters trailing. In order to disregard a ScopedRequest, you can do something like this:

if(request instanceof ScopedRequestImpl) {
ServletRequest sRequest = ((ScopedRequestImpl) request).getRequest();
request = ((ScopedRequestImpl) request).getOuterRequest();
}

Now you have the original request and you may add any parameters you like. Firstly though, be sure to add all parameters from the Map parameters in order to keep any state the framework put there in the first place.

Example use case


One use of this is to have the portlets react to a parameter on the request. Thereby allowing a bookmarkable URL to return to the same page, with the same information. One side effect of this is that back and forth will be working, since the browser history executes via URLs :).
The responsibility of maintaining the parameters is then up to the portlets themselves. I will leave it up to individual projects to implement a backbone for the portlets communication with the URL rewriter. But using session might be the easy way out..

Registering the URL rewriter


The URL rewriter is retrieved from the request by the framework, but we need a way of putting it there in the first place. The simple solution is to use a filter, so we'll do just that:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain
chain) throws IOException, ServletException {
request.setAttribute(UrlRewriterPlugin.URL_REWRITER_PLUGIN, new MyURLRewriter());
}

and then add the appropriate configuration in the web.xml:

<filter>
<filter-name>URLFilter</filter-name>
<filter-class>com.mycompany.URLFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>URLFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Prologue


These few tips should be enough for a developer to implement bookmarkability in a WebLogic portal server. But beware of this solution, since it is highly dependant on the BEA implementation. If any issues regarding bad URLs pop-up, I would refer you to the 'com.bea.portlet.GenericURL' class. See what it does if the URLRewriter is not used. I have implemented a great deal of security around the URLRewriter in order to ensure the state of the portlet and portal. The power of the URLRewriter is great though.. use with caution ;)

credit for an example of this goes to Deepak Natarajan