The first version of the Portlet Specification (JSR 168), regretfully, did not cover interaction between portlets on a page. The side-effect of diverting the issue to the subsequent release of the specification, has forced portal vendors to each craft their own proprietary API to achieve interportlet communication. This chapter covers the JBoss Portal Interportlet Communication API, and its ability to allow a developer to create links to other portal pages and affect behaviour in other portlets.
IPC requires that a listener be available to intercept requests and respond appropriately. To enable the listener, it has to be declared as an mbean in your service descriptor, as in our example under helloworldipcportlet.sar/META-INF/jboss-service.xml :
<server> <mbean code="org.jboss.portal.core.event.PortalEventListenerServiceImpl" name="portal:service=ListenerService,type=ipc_listener" xmbean-dd="" xmbean-code="org.jboss.portal.common.system.JBossServiceModelMBean"> <depends optional-attribute-name="Registry" proxy-type="attribute">portal:service=ListenerRegistry</depends> <xmbean/> <attribute name="RegistryId">ipc_listener</attribute> <attribute name="ListenerClassName">org.jboss.portlet.hello.HelloWorldPortletB$Listener</attribute> </mbean> </server>
It is important to note here, that you will most likely not have to change any of these values, except for the following:
Enabling IPC, also requires that the listener be declared in the *-object.xml, so the Portal recognizes it. The declaration happens at the page level, as in our example:
<deployments> <deployment> <parent-ref>default</parent-ref> <if-exists>overwrite</if-exists> <page> <page-name>IPC</page-name> <listener>ipc_listener</listener> <properties/> ...
Now that the listener is configured in the service descriptor and the portal can recognize it, we must create the appropriate listener class within our portlet.
In our example, Portlet A will be modifying the behaviour of Portlet B. So then we will declare the listener in Portlet B, the recipient portlet.
public static class Listener implements PortalNodeEventListener { public PortalNodeEvent onEvent(PortalNodeEventBubbler bubbler, PortalNodeEvent event) { PortalNode node = event.getNode(); // Get node name String nodeName = node.getName(); // See if we need to create a new event or not WindowActionEvent newEvent = null; if(nodeName.equals("HelloWorldPortletAWindow") && event instanceof WindowActionEvent) { // Find window B WindowActionEvent wae = (WindowActionEvent) event; PortalNode windowB = node.resolve("../HelloWorldPortletBWindow"); if(windowB != null) { // We can redirect and modify the view/mode as well! newEvent = new WindowActionEvent(windowB); newEvent.setMode(wae.getMode()); //newEvent.setWindowState(WindowState.MAXIMIZED); newEvent.setParameters(wae.getParameters()); } } if(newEvent != null) { // If we have a new event redirect to it return newEvent; } else { // Otherwise bubble up return bubbler.dispatch(event); } } }
It is important to note here some of the important items in this listener class. Logic used to determine if the requesting node was Portlet A.:
nodeName.equals("HelloWorldPortletAWindow")
Get the current window object so we can dispatch the event to it:
PortalNode windowB = node.resolve("../HelloWorldPortletBWindow");
Set the original parameter from Portlet A, so Portlet B can access them in its processAction():
newEvent.setParameters(wae.getParameters());
Modify Portlet B windowmode and/or windowstate (ie, Maximize and place in Edit Mode):
newEvent.setMode(wae.getMode()); // wae.setMode(Mode.EDIT); newEvent.setWindowState(wae.getWindowState()); // WindowState.MAXIMIZED
Creating a link from Portlet A to Portlet B occurs in the same way you would normally create a PortletURL:
html.append("<form action=\"" + resp.createActionURL() + "\" method=\"post\">"); html.append("<input type=\"text\" name=\"sometext\"><br/>"); html.append("<select name=\"color\">"); html.append("<option>blue</option>"); html.append("<option>red</option>"); html.append("<option>black</option>"); html.append("</select>"); html.append("<input type=\"submit\"/>"); html.append("</form>");
As you can see from the code above, we are creating a simple HTML form with an ActionURL from within Portlet A. It will target itself, but be intercepted by the listener, created in Portlet B. This form passes some text and a color value from input fields.
Now, in Portlet B, our listener innerclass will trigger the processAction() in Portlet B, reading in the parameters from the Portlet A form:
public void processAction(JBossActionRequest request, JBossActionResponse response) throws PortletException, PortletSecurityException, IOException { String color = request.getParameter("color"); String text = request.getParameter("sometext"); if(color != null && text != null) { response.setRenderParameter("color", color); response.setRenderParameter("sometext", text); } }
Setting the render parameters, it will now pass them on to the Portlet B, doView();
As seen in the included NavigationPortlet (tabs found at the top of the default layout), we are able to create links to Portal Pages. Included in the download sample , you can see the basics of creating a link to a Portal Page. I have added comments in-line to explain the code sample below:
// Get the ParentNode. Since we are inside a Window, the Parent is the Page PortalNode thisNode = req.getPortalNode().getParent(); // Get the Node in the Portal hierarchy tree known as "../default" PortalNode linkToNode = thisNode.resolve("../default"); // Create a RenderURL to the "../default" Page Node PortalNodeURL pageURL = resp.createRenderURL(linkToNode); // Output the Node's name and URL for users html.append("Page: " + linkToNode.getName() + " -> "); html.append("<a href=\"" + pageURL.toString() + "\">" + linkToNode.getName() + "</a>");