Developing portlets can be a frustrating experience, especially if you are used to technologies with lots of helpful information out there on the web. While lots of people are using portlets, it seems that few are writing about them. What should be a straightforward exercise in developing to a standard API can quickly become bogged down in the intricacies of the way your particular portal server works.
Many of these issues wouldn’t come up with simpler portlets, but portlets with AJAX bring them out.
Getting Started
I’ve found the Eclipse Portal Pack a good way to get started. It lets you create nice skeleton Portlet projects in eclipse, and export them as .wars when the time comes to deploy them. You can also arrange to have them deployed from within eclipse, however if you’re targetting a server (like Websphere Portal Server 6.1) that doesn’t have a deployment server option for eclipse yet, you’ll have to do it yourself. I recommend that you do deploy to your target Portal server, rather than use an embedded preview server, otherwise you might miss specific issues.
To actually get your code-test cycle turn around down to a reasonable length of time, you may have to adopt slightly more hacky ways of keeping the files on the development server up to date. One technique is to export and deploy the war, then use the file sync plugin to automatically copy changes from your WebContent folder to the deployed war folder (e.g. on Websphere this is usually C:\IBM\WebSphere\<profile>\installedApps\<cell>\<war name>.ear\<war name>.war). You can also get the build folder automatically copied to WEB-INF\classes.
State
Portlets were originally (JSR 168) designed with the old web interaction model in mind. All changes to a portlets state were expected to be accompanied by a full page reload. This isn’t a great interaction experience for the user even with a simple webpage, with portlets, where a small state change in a single portlet reloads every portlet on the page, it’s particularly bad.
In the first portlet API , the way you avoided this was by also deploying a servlet along with your portlet. The servlet is used to save the state of your portlet when it changes. In this architecture, the portlet gets its initial state and serves the portlet fragment. When the user makes a change on the client side (e.g. adds a column in an AJAX grid), in the background an XMLHttpRequest
is sent off to the servlet, which alters the state, so that next time the portlet is loaded, the portlet starts it with the correct state. You can read more about this technique here.
I recommend that anyone deploying an AJAX portlet at the moment uses JSR 286. This later version of the standard allows you to do away with the separate servlet, and do everything you need to do through the ResourceServingPortlet
interface. Inside the serveResource
method that you override, you have access to all the same PortletPreferences
and parameters that you have access to during the rendering which makes life easier.
Remember that renderResponse.createResourceURL()
returns a ResourceURL
object rather than a String
, so if you’re using it in a JSP, you’ll need to do <%=renderResponse.createResourceURL().toString()%>
, otherwise you’ll be scratching your head wondering why you can’t connect to the serveResource
method on your portlet.
Another big benefit of JSR 286 is that if you have the same portlet twice on a page, you can use request.getWindowID()
to give you a different ID for each, which allows you to store state separately for them. Other techniques tend to have the disadvantage that the scope of the identifiers you are given doesn’t exactly match what you need.
Using Libraries and CSS
In an ideal world, every portlet loads exactly what it needs for itself, and doesn’t affect other portlets on the page. In the browser however, they are all sharing the same DOM. In particular, the global Javascript namespace and the CSS styles can easily overlap with each other. Portlets only really offer one way of mitigating this, which is the <portlet:namespace/>
giving you an incomprehensible string guaranteed to be unique on the page. This is suitable for javascript function or variable names, or DOM element IDs.
However, you can’t put <portlet:namespace/>
tags in external JS files, and ideally you only want to load them once anyway. It might seem that the new section PLT.12.5.4 Setting Markup Head Elements
would allow you to set SCRIPT
tags in the header, avoiding the problem of loading the scripts more than once, as each portlet to use the library is loaded. In practice, this is an optional section of the specification, and of the portal servers we’ve tried, some support it for META
tags, but none support it for SCRIPT
tags.
In practice, what seems to work most reliably is to use javascript to conditionally load your Javascript files and CSS if they aren’t already there. Until the next version of Javascript is widely used, there isn’t a standard way of loading more javascript from inside javascript, so if you haven’t already got a preferred way, make sure that whatever you choose works in all the browsers you want to support (be aware: some techniques don’t guarantee that the javascript files are loaded in the specified order in some browsers).
In particular, bear in mind that some portal servers have a client-side-aggregation option, which moves most of the portlet management to the client. If you’re using this (it’s a particular theme in WPS), it’s unlikely that any javascript loading techniques that use document.write
will work. document.write
usually only works correctly during the initial loading of the document. After loading has finished, the document is closed, and any further document.write
s will blank the page.
An example that checks to see if it has already been loaded, and can then be used to load other script files, might start like this:
if ( window.PortletUtils === undefined ) { PortletUtils = {}; PortletUtils.loadedScripts = {}; PortletUtils.load = function (scriptfile) { if (!PortletUtils.loadedScripts[scriptfile]) { PortletUtils.loadedScripts[scriptfile] = true; PortletUtils.writeScriptTag(scriptfile); } ....
And then, further JS files can be loaded with PortletUtils.load("<%=contextPath%>/JSLibrary.js");
ensuring that they are loaded only the first time a portlet is encountered that needs them.
Another way of achieving the same thing is to put the script tags inside the theme itself. This of course means that your portlet will only work in themes you have edited to load the resources you need – a bad blow to your ability to deploy the portlet, however it also places the script loading in the place that people expect it, meaning that the pause is at the beginning of the rendering, rather than freezing the page as it starts to render each portlet.
You may well find that in order to get the page to display as you want you’ll have to change the theme or skin. On Pluto, we needed to change the container page to render in strict mode to get the compatibility we needed across the different browsers. On both Pluto and Websphere, the default is to have a large margin between the portlet frame and the portlet content, which wasn’t appropriate for our use.
Comet
If you’re doing streaming, it’s quite likely that you’ll be setting your document.domain
to allow you to load data from servers hosted within your domain, but not actually running on the same server as your web server. Something to watch out for here is that if the theme/skin that the portal server is configured with contains iframes, setting the document domain for your window will break communication the skin has with its iframes. The solution is either to use a skin that doesn’t use iframes, or to make each of the embedded iframe set its document.domain
too.
Resources
There is a dearth of Portlet resources online, however here are some that we have found helpful.
- Best Practices for Applying Ajax to JSR 168 Portlets (but note, that it’s talking about JSR 168 rather than JSR 286.
- JSR Specification
- Apache Pluto, the reference implementation of a portal server
- Liferay Portal, a well respected open source portal
- Portlet API v2, an online version of the jsdoc.