JBoss.orgCommunity Documentation
JBoss Portal provides an Application Programming Interface (API) which allows to write code that interacts with the portal. The life time and validity of the API is tied to the major version which means that no changes should be required when code is written against the API provided by the JBoss Portal 2.x versions and used in a later version of JBoss Portal 2.x.
The Portal API package prefix is org.jboss.portal.api. All of the classes that are part of this API are prefixed with this package name except for the org.jboss.portal.Mode and org.jboss.portal.WindowState classes. These two classes were defined before the official Portal API framework was created and so the names have been maintained for backward compatibility.
The Portlet API defines two classes that represent a portion of the visual state of a Portlet which are javax.portlet.PortletMode and javax.portlet.WindowState. Likewise the Portal API defines similar classes named org.jboss.portal.Mode and org.jboss.portal.WindowState which offer comparable characteristics, the main differences are:
Usage of factory methods to obtain instances.
Classes implements the java.io.Serializable interface.
The Mode class
The WindowState class
In the Portal API, the Mode interface is named like this because it does represent the mode of some visual object. The Portlet API names it PortletMode because it makes the assumption that the underlying object is of type Portlet.
There are times when a portlet needs to signal the portal or share information with it. The portal is the only authority to decide if it will take into account that piece of information or ignore it. In JBoss Portal we use as much as possible the mechanisms offered by the portlet spec to achieve that communication.
If a portlet desires to sign out the user, it can let the portal know by triggering a JSR-286 portlet event. To do so, simply defines the event "signOut" in the namespace "urn:jboss:portal" as a publishing event. In the action phase of the portlet, trigger the event, as a payload you can specify a redirection URL. If the payload is null, it will redirect the user to the default page of the default portal. See the following snippet to use in the action phase, it will ask the portal to sign out the user and redirect him to the JBoss Portal blog:
QName name = new QName("urn:jboss:portal", "signOut"); response.setEvent(name, "http://blog.jboss-portal.org");
The JSR-286 specification introduced a new phase for setting up the HTML headers. It is commonly used to add stylesheets
and javascript to the page. An extension of it for JBoss Portal lets you define the web browser title.
To define the web browser title, a portlet simply needs to define a new header element "title". This could be done by a portlet overriding
the method doHeaders(RenderRequest req, RenderResponse resp)
to add such an element.
public void doHeaders(RenderRequest req, RenderResponse resp) { Element element = resp.createElement("title"); element.setTextContent("My new web browser title"); resp.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, element); }
It several portlets on a page defines a web browser title, only one of them will be displayed. We can consider that the title to be displayed will be randomly chosen.
The Portal API defines the org.jboss.portal.api.PortalURL interface to represent URL managed by the portal.
The PortalURL interface
The setAuthenticated(Boolean wantAuthenticated) methods defines if the URL requires the authentication of the user. If the argument value is true then the user must be authenticated to access the URL, if the argument value is false then the user should not be authenticated. Finally if the argument value is null then it means that the URL authenticated mode should reuse the current mode.
The setSecure(Boolean wantSecure) methods defines the same as above but for the transport guarantee offered by the underlying protocol which means most of the time the secure HTTP protocol (HTTPS).
The setRelative(boolean relative) defines the output format of the URL and whether the created URL will be an URL relative to the same web server or will be the full URL.
The toString() method will create the URL as a string.
The PortalSession interface
It is possible to have access to a portion of the portal session to store objects. The org.jboss.portal.api.session.PortalSession interface defines its API and is similar to the javax.servlet.http.HttpSession except that it does not offer methods to invalidate the session as the session is managed by the portal.
The PortalRuntimeContext interface
The org.jboss.portal.api.PortalRuntimeContext gives access to state or operations associated at runtime with the current user of the portal. The String getUserId() retrieve the user id and can return null if no user is associated with the context. It also gives access to the PortalSession instance associated with the current user. Finally it gives access to the NavigationalStateContext associated with the current user.
The portal structure is a tree formed by nodes. It is possible to programmatically access the portal tree in order to
discover the tree structure of the portal
create URL that will render the different portal nodes
access the properties of a specific node
The PortalNode interface
As usual with tree structures, the main interface to study is the org.jboss.portal.api.node.PortalNode. That interface is intentionally intended for obtaining useful information from the tree. It is not possible to use it to modify the tree shape because it is not intended to be a management interface.
public interface PortalNode { int getType(); String getName(); String getDisplayName(Locale locale); Map getProperties(); PortalNodeURL createURL(PortalRuntimeContext portalRuntimeContext); ... }
The interface offers methods to retrieve informations for a given node such as the node type, the node name or the properties of the node. The noticeable node types are:
PortalNode.TYPE_PORTAL : the node represents a portal
PortalNode.TYPE_PAGE : the node represents a portal page
PortalNode.TYPE_WINDOW : the node represents a page window
The org.jboss.portal.api.node.PortalNodeURL is an extension of the PortalURL interface which adds additional methods useful for setting parameters on the URL. There are no guarantees that the portal node will use the parameters. So far portal node URL parameters are only useful for nodes of type PortalNode.TYPE_WINDOW and they should be treated as portlet render parameters in the case of the portlet is a local portlet and is not a remote portlet. The method that creates portal node URL requires as parameter an instance of PortalRuntimeContext.
The interface also offers methods to navigate the node hierarchy:
public interface PortalNode { ... PortalNode getChild(String name); Collection getChildren(); PortalNode getRoot(); PortalNode getParent(); ... }
The navigational state is a state managed by the portal that associates to each user the state triggered by its navigation. A well known part of the navigational state are the render parameters provided at runtime during the call of the method void render(RenderRequest req, RenderResponse resp). The portal API offers an interface to query and update the navigational state of the portal. For now the API only exposes mode and window states of portal nodes of type window.
The NavigationalStateContext interface
Portal events are a powerful mechanism to be aware of what is happening in the portal at runtime. The base package for event is org.jboss.portal.api.event and it contains the common event classes and interfaces.
The PortalEvent class
The org.jboss.portal.api.event.PortalEvent abstract class is the base class for all kind of portal events.
The PortalEventContext interface
The org.jboss.portal.api.event.PortalEventContext interface defines the context in which an event is created and propagated. It allows retrieval of the PortalRuntimeContext which can in turn be used to obtain the portal context.
The PortalEventListener interface
The org.jboss.portal.api.event.PortalEventListener interface defines the contract that class can implement in order to receive portal event notifications. It contains the method void onEvent(PortalEvent event) called by the portal framework.
Listeners declaration requires a service to be deployed in JBoss that will instantiate the service implementation and register it with the service registry. We will see how to achieve that in the example section of this chapter.
The event propagation model uses one instance of a listener class to receive all portal events that may be routed to that class when appropriate. Therefore implementors needs to be aware of that model and must provide thread safe implementations.
Portal node events extend the abstract portal event framework in order to provide notifications about user interface events happening at runtime. For instance when the portal renders a page or a window, a corresponding event will be fired.
The portal node event class hierarchy
The org.jboss.portal.api.node.event.PortalNodeEvent class extends the org.jboss.portal.api.node.PortalEvent class and is the base class for all events of portal nodes. It defines a single method PortalNode getNode() which can be used to retrieve the node targetted by the event.
The org.jboss.portal.api.node.event.WindowEvent is an extension for portal nodes of type window. It provides access to the mode and window state of the window. It has 3 subclasses which represent different kind of event that can target windows.
The org.jboss.portal.api.node.event.WindowNavigationEvent is fired when the window navigational state changes. For a portlet it means that the window is targetted by an URL of type render.
The org.jboss.portal.api.node.event.WindowActionEvent is fired when the window is targetted by an action. For a portlet it means that the window is targetted by an URL of type action.
The org.jboss.portal.api.node.event.WindowRenderEvent is fired when the window is going to be rendered by the portal.
The org.jboss.portal.api.node.event.PageEvent is an extension for portal nodes of type page.
The org.jboss.portal.api.node.event.PageRenderEvent is fired when the page is going to be rendered by the portal.
A portal node event is fired when an event of interest happens to a portal node of the portal tree. The notification model is comparable to the bubbling propagation model defined by the DOM specification. When an event is fired, the event is propagated in the hierarchy from the most inner node where the event happens to the root node of the tree.
The portal node event propagation model
The org.jboss.portal.api.node.event.PortalNodeEventListener interface should be used instead of the too generic org.jboss.portal.api.event.PortalEventListener when it comes down of listening portal node events. Actually it does not replace it, the PortalEventListener interface semantic allows only traditional event delivering. The PortalNodeEventListener interface is designed to match the bubbling effect during an event delivery.
The PortalNodeEvent onEvent(PortalNodeEventContext context, PortalNodeEvent event) method declares a PortalNodeEvent as return type. Commonly the method returns null; however, a returned PortalNodeEvent replaces the event in the listeners subsequently called during the event bubbling process.
The PortalNodeEventContext interface
The org.jboss.portal.api.node.event.PortalNodeEventContext interface extends the PortalEventContext interface and plays an important role in the event delivery model explained in the previous section. That interface gives full control over the delivery of the event to ascendant nodes in the hierarchy, even more it gives the possibility to replace the current event being delivered by a new event that will be transformed into the corresponding portal behavior. However there are no guarantees that the portal will turn the returned event into a portal behavior, here the portal provides a best effort policy, indeed sometime it is not possible to achieve the substitution of one event by another.
Here the simplest implementation of a listener that does nothing except than correctly passing the control to a parent event listener if there is one.
public PortalNodeEvent onEvent(PortalNodeEventContext context, PortalNodeEvent event) { return context.dispatch(); }
The method PortalNode getNode() returns the current node being selected during the event bubbler dispatching mechanism.
The life cycle of the session of the portal associated with the user can also raise events. This kind of event is not bound to a portal node since it is triggered whenever a portal session is created or destroyed
The PortalSessionEvent class
There are two different types of events:
org.jboss.portal.api.session.event.PortalSessionEvent.SESSION_CREATED, fired when a new portal session is created
org.jboss.portal.api.session.event.PortalSessionEvent.SESSION_DESTROYED, fired when a new portal session is destroyed
The life cycle of the portal user can also raise events such as its authentication. A subclass of the wider scope UserEvent class is provided and triggers events whenever a user signs in or out. The UserEvent object gives access to the user name of the logged-in user through the method String getId().
The UserEvent class and UserAuthenticationEvent sub-classes
The UserAuthenticationEvent triggers two events that can be catched:
org.jboss.portal.api.session.event.UserAuthenticationEvent.SIGN_IN, fired when a portal user signs in
org.jboss.portal.api.session.event.UserAuthenticationEvent.SIGN_OUT, fired when a portal user signs out
Based on the UserEvent class other custom user related events could be added like one that would trigger when a new user is being registered
The events mechanism is quite powerful, in this section of the chapter we will see few simple examples to explain how it works.
In this example, we will create a simple counter of the number of logged-in registered users. In order to do that we just need to keep track of Sign-in and Sign-out events.
First, let's write our listener. It just a class that will implement org.jboss.portal.api.event.PortalEventListener and its unique method void onEvent(PortalEventContext eventContext, PortalEvent event). Here is such an example:
package org.jboss.portal.core.portlet.test.event; import[...] public class UserCounterListener implements PortalEventListener { /** Thread-safe long */ private final SynchronizedLong counter = new SynchronizedLong(0); /** Thread-safe long */ private final SynchronizedLong counterEver = new SynchronizedLong(0); public void onEvent(PortalEventContext eventContext, PortalEvent event) { if (event instanceof UserAuthenticationEvent) { UserAuthenticationEvent userEvent = (UserAuthenticationEvent)event; if (userEvent.getType() == UserAuthenticationEvent.SIGN_IN) { counter.increment(); counterEver.increment(); } else if (userEvent.getType() == UserAuthenticationEvent.SIGN_OUT) { counter.decrement(); } System.out.println("Counter : " + counter.get()); System.out.println("Counter ever: " + counterEver.get()); } } }
On this method we simply filter down to UserAuthenticationEvent then depending on the type of authentication event we update the counters. counter keeps track of the registered and logged-in users, while counterEver only counts the number of times people logged-in the portal.
Now that the Java class has been written we need to register it so that it can be called when the events are triggered. To do so we need to register it as an MBean. It can be done by editing the sar descriptor file: YourService.sar/META-INF/jboss-service.xml so that it looks like the following:
<?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.jboss.portal.core.event.PortalEventListenerServiceImpl" name="portal:service=ListenerService,type=counter_listener" xmbean-dd="" xmbean-code="org.jboss.portal.jems.as.system.JBossServiceModelMBean"> <xmbean/> <depends optional-attribute-name="Registry" proxy-type="attribute">portal:service=ListenerRegistry</depends> <attribute name="RegistryId">counter_listener</attribute> <attribute name="ListenerClassName"> org.jboss.portal.core.portlet.test.event.UserCounterListener </attribute> </mbean> </server>
This snippet can be kept as it is, providing you change the values:
name: Must follow the pattern: portal:service=ListenerService,type={{UNIQUENAME}}
RegistryId: Must match the type (here: counter_listener)
ListenerClassName: Full path to the listener (here: org.jboss.portal.core.portlet.test.event.UserCounterListener).
That's it - we now have a user counter that will display it states each time a user logs-in our logs-out.
The first version of the Portlet Specification (JSR 168), regretfully, did not cover interaction between portlets. 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 inter portlet communication. Here we will see how we can use the event mechanism to pass parameters from one portlet to the other (and only to the other portlet).
The overall scenario will be that Portlet B will need to be updated based on some parameter set on Portlet A. To achieve that we will use a portal node event.
Portlet A is a simple Generic portlet that has a form that sends a color name:
public class PortletA extends GenericPortlet { protected void doView(RenderRequest request, RenderResponse response) throws PortletException, PortletSecurityException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<form action=\"" + response.createActionURL() + "\" method=\"post\">"); writer.println("<select name=\"color\">"); writer.println("<option>blue</option>"); writer.println("<option>red</option>"); writer.println("<option>black</option>"); writer.println("</select>"); writer.println("<input type=\"submit\"/>"); writer.println("</form>"); writer.close(); } }
The other portlet (Portlet B) that will receive parameters from Portlet A is also a simple Generic portlet:
public class PortletB extends GenericPortlet { public void processAction(ActionRequest request, ActionResponse response) throws PortletException, PortletSecurityException, IOException { String color = request.getParameter("color"); if (color != null) { response.setRenderParameter("color", color); } } protected void doView(RenderRequest request, RenderResponse response) throws PortletException, PortletSecurityException, IOException { String color = request.getParameter("color"); response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<div" + (color == null ? "" : " style=\"color:" + color + ";\"") + ">some text in color</div>"); writer.close(); } // Inner listener explained after }
With those two portlets in hands, we just want to pass parameters from Portlet A to Portlet B (the color in as a request parameter in our case). In order to achieve this goal, we will write an inner Listener in Portlet B that will be triggered on any WindowActionEvent of Portlet A. This listener will create a new WindowActionEvent on the window of Portlet B.
public static class Listener implements PortalNodeEventListener { public PortalNodeEvent onEvent(PortalNodeEventContext context, 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("PortletAWindow") && event instanceof WindowActionEvent) { // Find window B WindowActionEvent wae = (WindowActionEvent)event; PortalNode windowB = node.resolve("../PortletBWindow"); if (windowB != null) { // We can redirect newEvent = new WindowActionEvent(windowB); newEvent.setParameters(wae.getParameters()); newEvent.setMode(wae.getMode()); newEvent.setWindowState(WindowState.MAXIMIZED); // Redirect to the new event return newEvent; } } // Otherwise bubble up return context.dispatch(); } }
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("PortletAWindow")
Get the current window object so we can dispatch the event to it:
PortalNode windowB = node.resolve("../PortletBWindow");
Set the original parameter from Portlet A, so Portlet B can access them in its processAction():
newEvent.setParameters(wae.getParameters());
We still need to register our listener as an mbean:
<mbean code="org.jboss.portal.core.event.PortalEventListenerServiceImpl" name="portal:service=ListenerService,type=test_listener" xmbean-dd="" xmbean-code="org.jboss.portal.jems.as.system.JBossServiceModelMBean"> <xmbean/> <depends optional-attribute-name="Registry" proxy-type="attribute">portal:service=ListenerRegistry</depends> <attribute name="RegistryId">test_listener</attribute> <attribute name="ListenerClassName"> org.jboss.portal.core.samples.basic.event.PortletB$Listener </attribute> </mbean>
For node events, we also need to declare on which node we want to listen, this is done by modifying
the *-object.xml
that defines your portal nodes. In this example we want to trigger
the listener each time the window containing the portlet A is actioned. We can add the listener
tag to specify that out listener with RegistryId
=test_listener should be triggered
on events on the embedding object.
... <window> <window-name>PortletAWindow</window-name> <instance-ref>PortletAInstance</instance-ref> <region>center</region> <height>0</height> <listener>test_listener</listener> </window> ...
Of course we could have added it at the page level instead of the window level. Note that a unique listener can be specified, the event mechanism is primarily done to let the developer change the navigation state of the portal, this example being a nice side-effect of this feature.
The portlet 2.0 specification (JSR 286) will cover Inter Portlet Communication so that portlets using it can work with different portal vendors.
Linking to some other pages or portals is also out of the scope of the portlet specification. As seen previously JBoss Portal offers an API in order to create links to other portal nodes. The JBoss request gives access to the current window node from which we can navigate from.
// 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>");
From this, it is easy to create a menu or sitemap, the List getChildren() method will return all the child nodes on which the user has the view right access.
Those examples are available in the core-samples package in the sources of JBoss Portal. There are more examples of events usage in the samples delivered with JBoss Portal. One of them shows the usage of a portal node event to only have one window in normal mode at a time in a region. Anytime another window is being put in normal mode, all the other windows of the same regions are automatically minimized.