Understanding Portlet 2.0 (JSR 286) - Part 2
In my previous blog entry (Part 1), I mentioned that one of the key limitations of the Portlet 1.0 spec was that it did not provide a mechanism for one portlet to "notify" another portlet that it's state has changed. Thankfully, Portlet 2.0 provides (not one but two!) mechanisms by which portlets can communicate with each other.
For the sake of illustration consider a portal page with two portlets : A user profile portlet (Profile portlet) which stores the personal details of the logged in user and a Weather portlet which shows the weekly weather forecast for a particular location. Our usecase is that when the user updates her profile address to a new location, the Weather portlet needs to show the weather forecast for that particular location.
In Portlet 1.0, developers are left with no choice but to use a shared memory space. When the first portlet is being acted upon , in this case the Profile Portlet is clicked, it stores the data that needs to be communicated into a shared memory space. When the portlet that needs to receive this data is being rendered, it is programmed to read the data from the same shared memory. Here is a quick illustration.
In processAction() of ProfilePortlet, data is written to shared memory:
During render() of WeatherPortlet, data is read from the shared memory:
So, what is this shared memory space ? Well, if you bundle these two portlet into a single portlet application (i.e a single war file sharing the same portlet.xml) there are two easy choices: The PortletSession and the PortletContext.
Here, the notion of sessions and context is very similar to (you could even say copied from :) the Servlet world. A PortletSession exists per user per portlet application (like HttpSession exists per user per web application) and a PortletContext is shared between all users and all portlets for a given portlet application (ServletContext is shared between all users and servlets in a web application). However, Portlets additionally possess a mechanism to conveniently namespace an attribute that is placed in the session. This mechanism is called "scoping". The scoping mechanism helps a portlet to easily differentiate between data that is used exclusively by itself (referred to as Portlet-scoped data) and data that is shared between it and other portlets in the application (referred to a Application-scoped data).
While both the PortletSession and PortletContext can be used to solve the interportlet communication problem, this scenario occurs most frequently in the context of a user accessing a portal. Hence it is typical to use the PortletSession.( And though PortletContext can be used when you want the data to be propagated to *all* users – there might be a better way of handling such a case).
The source code would look like this:
In processAction of ProfilePortlet :
portletSession.setAttribute("location", newLocation, PortletSession.APPLICATION_SCOPE);
In render of WeatherPortlet:
currentLocation = (String) portletSession.getAttribute("location",PortletSession.APPLICATION_SCOPE);
showWeatherForThisLocation(currentLocation);
There are a bunch of problems with this approach. Firstly, using the PortletSession (or PortletContext) is only possible when the portlets are packaged as a part of the same portlet application. For portlets that are part of different portlet applications, this won't work -since the sessions and contexts are different. Hence, in such cases, you need to resort to other standard communication mechanisms JMS (messaging), JNDI etc. And such solutions are far from tirvial and sometime becomes an overkill for the problem we are trying to address.
Secondly, the Weather Portlet is limited by the semantics of the portlet programming model, on how it can respond to the communicated data. This is because portlets are not allowed to change their states during a render()(as rendering is defined as idempotent). And the Weather Portlet is only rendered and never acted upon. Hence it will not be able to change it's window state, mode or preferences during the above interaction. This is seriously limiting.
Thirdly, there is no way for the Weather portlet to communicate back to the Profile portlet (or any other portlet), if at all it wants to. The above communication is one-time and unidirectional.
Finally, from a design perspective, this is probably not what a session (or context ) is meant to be used for. And this could lead to bad code. (As developers might develop the tendency to put all transient data into the HttpSession just because it is easier to share it between multiple portlets)
Portlet 2.0 introduces a full-fledged interportlet communication mechanism called Eventing. Eventing can be described as a loosely coupled, brokered means of communication between portlets. It is loosely coupled because portlets that send events need not have any compile-time or runtime dependency on the portlets that receive that event (or vice versa). It is brokered because the portal/portlet container acts as a conduit for these events.
The simplest way to understand eventing is to consider the portlet-container as an "event bus". Portlets that need to send notifications to other portlets publish events. Portlets that are interested in receiving notifications from others subscribe to events.
To illustrate eventing, let's take the same example as above and see how we would implement a Portlet 2.0 solution. First step, the user clicks on the Profile portlet and changes her location.This triggers a processAction() on the Profile portlet and the`portlet updates it's state.At the end of this processAction() phase, the portlet does something different. Instead of writing to a`shared memory, like the previous example, it requests the portlet-container to send a "location changed" event.
The next phase is called the eventing phase. This is when the portlet-container determines which other portlets needs to receive this event and routes it appropriately. In this case, we assume that the Weather portlet was configured to consume this event, hence the event sent to the Weather Portlet.
The next is the render phase. Notice that in this case, the portlet doesn't need to do anything special. It just needs to render itself, according to it's current state.
In the above illustration, it is pretty easy to notice that Portlet 2.0 has solved the problem by introducing a separate new lifecycle phase - the eventing phase. So our original lifecycle diagram can be modified to this now:
Eventing always occurs after processAction(). It also always occurs before the render(). And eventing can modify state (i.e it is a non-idempotent operation). This solves most of the shortcomings we noticed in the Portlet 1.0 implementation.
Since eventing does not rely on any shared memory space, even portlets from different portlet applications can communicate with each other. Also, since eventing is defined as a state-modifying operation, the portlet is free to make an any persistent change to it's state including changing it's mode or window state, storing user preferences or modifying a database backend. Changes to it's UI will be reflected in the rendering phase that follows.
Another interesting aspect is that the Weather Portlet is free to respond to the "location changed" event by sending it's own new event. And if the Profile portlet is configured to receive it, it will. In other words, a first generation event can trigger a second generation event can trigger a third generation event... and so on.
Posted at 04:21PM Feb 14, 2007 by Navaneeth Krishnan in Portlet | Comments[5]
This work is licensed under a
Creative Commons Attribution-NonCommercial-NoDerivs 2.5 License.
Posted by Rickard Öberg on February 16, 2007 at 01:30 PM IST #
Posted by Navaneeth on February 16, 2007 at 02:20 PM IST #
Posted by Rickard Öberg on February 16, 2007 at 02:25 PM IST #
Posted by Navaneeth on February 16, 2007 at 03:18 PM IST #
Posted by Deepak on February 20, 2007 at 04:40 PM IST #