Showing posts with label jsr286. Show all posts
Showing posts with label jsr286. Show all posts

The actionScopedRequestAttributes container runtime option

One common issue in working with standard compliant portlet is how do i pass data from action phase(processAction()) to render phase (render()). In order to take advantage of portlet life cycle normally we should execute the business logic to get data from back end in the processAction() method and then pass the complex object from processAction() to render() method but the problem is the we cannot pass complex object we can only pass string parameters by calling PortletResponse.setRenderParameter() method, only alternative is to use the PortletSession

The Portlet Specification introduced concept of container run time options that allows us to modify/ tune behavior of the portlet container. It introduced a javax.portlet.actionScopedRequestAttributes which is set to false by default, but if you set its value to true then you can set request attribute in the processAction() method and you should be able to access those request attributes in the render() method. These request attributes will be preserved until the next action or event request. One drawback of this approach is the request attributes are stored in PortletSession under the hood and it has performance implications of storing data in portlet session, but positive side this approach is better than storing and removing objects in session by yourself.


I wanted to try this feature so I did create ActionScopeRequestAttributePortelt portlet.

This is how my portlet.xml file looks like

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="com.webspherenotes.jsr286.ActionScopeRequestAttributePortlet.78dbf213e2">
<portlet>
<portlet-name>ActionScopeRequestAttribute</portlet-name>
<display-name xml:lang="en">ActionScopeRequestAttribute</display-name>
<display-name>ActionScopeRequestAttribute</display-name>
<portlet-class>com.webspherenotes.jsr286.ActionScopeRequestAttributePortlet</portlet-class>
<init-param>
<name>wps.markup</name>
<value>html</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>ActionScopeRequestAttribute</title>
<short-title>ActionScopeRequestAttribute</short-title>
<keywords>ActionScopeRequestAttribute</keywords>
</portlet-info>
</portlet>
<default-namespace>http://ActionScopeRequestAttribute/</default-namespace>
<container-runtime-option>
<name>javax.portlet.actionScopedRequestAttributes</name>
<value>true</value>
<value>numberOfCachedScopes</value>
<value>10</value>
</container-runtime-option>
</portlet-app>


As you can see the value of actionScopedRequestAttributes is set to true. THis is how the portlet code looks like

package com.webspherenotes.jsr286;

import java.io.IOException;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class ActionScopeRequestAttributePortlet extends javax.portlet.GenericPortlet {
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
System.out.println("doView(), Exiting Value of request attribute"
+ request.getAttribute("userName"));

PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/action.jsp");
rd.include(request,response);

}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
System.out.println("processAction(), Exiting Value of request attribute"
+ request.getAttribute("userName"));
System.out.println("Setting request attribute userName to "
+ request.getParameter("userName"));
request.setAttribute("userName", request.getParameter("userName"));
}

}


In the processAction() method i am storing the value of userName submitted by the user as request attribute and then in the .jsp i am just reading the values from renderRequest and display it to user like this


<%@page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<%@taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%>
<portlet:defineObjects />
<portlet:actionURL var="actionUrl">
<portlet:param name="action" value="submit" />
</portlet:actionURL>
<form method="post" action="<%=actionUrl %>">
<table>
<tr>
<td>Value stored in request attribute</td>
<td colspan="2">
<%=renderRequest.getAttribute("userName") %>
</td>
</tr>
<tr>
<td>User Name</td>
<td><input type="text" name="userName" /></td>
<td><input type="submit" name="submit" value="Submit" /></td>
</tr>

</table>
</form>


I tried this code in WPS 7.0 and it worked, so i used the How to inspect the values stored in the Portlet Session to check the PortletSession and i can see the com.ibm.ws.portletcontainer.core.action_scope_cache attribute in the PORTLET_SCOPE

Setting headers and cookies through portlet

You can use the two phase rendering concept to set both headers and cookies through a portlet, I wanted to try that so i built this TwoPhaseRenderingPortlet.java, you can download the sample code from here


package com.ibm.webspherenotes.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.http.Cookie;

public class TwoPhaseRenderingPortlet extends GenericPortlet {
protected void doHeaders(RenderRequest request, RenderResponse response) {
System.out.println("Entering TwoPhaseRenderingPortlet.doHeaders()");

Cookie c = new Cookie("myCookieName", "myCookieValue");
c.setPath(request.getContextPath());
response.addProperty(c);

response.setProperty("myHeaderName", "myHeaderValue");

System.out.println("Exiting TwoPhaseRenderingPortlet.doHeaders()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering TwoPhaseRenderingPortlet.doView()");
response.setContentType("text/html");
response.getWriter().println("Hello from two phase rendering portlet");
System.out.println("Exiting TwoPhaseRenderingPortlet.doView()");
}
}


The Response.setProperty() method is used for setting header and you can use it even for setting cookie. But for setting cookie there is specialized Response.setProperty() method that takes cookie object

Including JavaScript file in the Head of the page from portlet

One of the common requirement for portlet developer is how do i include a JavaScript file in the Head section of portal response page. I used the concept of Two phase rendering to build a sample portlet that includes JavaScript file in the head of the portal response.

This is how my TwoPhaseRenderPortlet.java looks like

package com.ibm.webspherenotes.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.MimeResponse;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.w3c.dom.Element;

public class TwoPhaseRenderingPortlet extends GenericPortlet{
protected void doHeaders(RenderRequest request, RenderResponse response) {
System.out.println("Entering TwoPhaseRenderingPortlet.doHeaders()");
Element script = response.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", request.getContextPath()+"/js/test.js");
response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, script);

System.out.println("Exiting TwoPhaseRenderingPortlet.doHeaders()");
}
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering TwoPhaseRenderingPortlet.doView()");
response.setContentType("text/html");
response.getWriter().println("Hello from two phase rendering portlet");
System.out.println("Exiting TwoPhaseRenderingPortlet.doView()");
}
}


In the doHeaders() method i am creating a script element and setting its src attribute to point to test.js which is part of the portlet web application.
This approach is same as that i used for changing portal page title

When i access the page with the portlet, i can see the test.js being called, i kept this invalid URL so that its easier to see in firebug

Changing title of portal page

The JSR 286 specification has a concept of two phase rendering, you can use it to add element in the portal document's head section, one use case for doing that would be if you want to change the title of the portal page.

I wanted to try that so i created this TwoPhaseRenderingPortlet.java like this

package com.ibm.webspherenotes.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.MimeResponse;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.w3c.dom.Element;

public class TwoPhaseRenderingPortlet extends GenericPortlet{
protected void doHeaders(RenderRequest request, RenderResponse response) {
System.out.println("Entering TwoPhaseRenderingPortlet.doHeaders()");

Element title = response.createElement("title");
title.setTextContent("www.webspherenotes.com portal page");
response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, title);

System.out.println("Exiting TwoPhaseRenderingPortlet.doHeaders()");
}
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering TwoPhaseRenderingPortlet.doView()");
response.setContentType("text/html");
response.getWriter().println("Hello from two phase rendering portlet");
System.out.println("Exiting TwoPhaseRenderingPortlet.doView()");
}
}


In the TwoPhaseRenderingPortlet, i am overriding doHeaders() method and inside the method, first i am creating a title element then i am setting the value of that element to www.webspherenotes.com portal page and the last step is to call response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, title); method which asks the RenderReponse object to insert newly created title element in the head section of the document.

Once the portlet is deployed i tried accessing it and i can see that the title that i set is displayed in the title bar of the browser, also when i tried looking at the source code i could see that the <title> element is added

Enable two phase rendering in portlet

Starting from JSR 286 (Portlet Specification 2.0), portlets support two phase rendering and it defines doHeaders() method that you can implement if you want to do one of the following things


  • Change title of the portal page

  • Add header

  • Add cookie



The two phase request processing is disabled by default, you can enable it by setting value of javax.portlet.renderHeaders container run time option to true like this


<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="v2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>Two phase rendering portlet</portlet-name>
<portlet-class>com.ibm.webspherenotes.jsr286.TwoPhaseRenderingPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>

</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>Two phase rendering Portlet</title>
<short-title>Two phase rendering Portlet</short-title>
<keywords>twophase</keywords>
</portlet-info>
</portlet>
<container-runtime-option>
<name>javax.portlet.renderHeaders</name>
<value>true</value>
</container-runtime-option>

</portlet-app>

WebSphere portal server managed custom Portlet modes

A preference is modified by using setValue. This normally occurs at the personalized layer and therefore affects only the current user. WebSphere Portal uses two special custom modes from the set of predefined custom modes in the Java Portlet Specification to allow setting up the more general preference levels:


  • The edit_defaults custom portlet mode is used to work directly on the shared preferences. In this case the personalized preferences level is not available.

  • Similarly, the config mode is used to read and modify the administrator level of preferences.

  • The deployment descriptor level of preferences can only change when the portlet is redeployed with a modified portlet.xml. It cannot be modified by portlet code.



I built a sample portlet to demonstrate how you can use the edit_defaults and config mode supported by WebSphere Portal Server.

package com.webspherenotes.portlet.jsr286;

import java.io.IOException;
import java.util.ArrayList;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletPreferences;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class WPSCustomPortletMode extends GenericPortlet{

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.doView()");
response.setContentType("text/html");
response.getWriter().println("User Name "
+ request.getPreferences().getValue("userName", "Not Set"));
System.out.println("Exiting CustomPortletMode.doView()");
}

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.processAction()");
String userName = request.getParameter("userName");
PortletPreferences preference = request.getPreferences();
preference.setValue("userName", userName);
preference.store();
System.out.println("Entering CustomPortletMode.processAction()");
}

protected void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.doEdit()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting CustomPortletMode.doEdit()");
}


protected void doEditDefaults(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
System.out.println("Entering CustomPortletMode.doEditDefaults()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting CustomPortletMode.doEdit()");
System.out.println("Entering CustomPortletMode.doEditDefaults()");
}
protected void doConfig(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
System.out.println("Entering CustomPortletMode.doConfig()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting CustomPortletMode.doConfig()");

}

PortletMode configMode = new PortletMode("config");
PortletMode editDefaultsMode = new PortletMode("edit_defaults");
protected void doDispatch(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.doDispatch");
System.out.println("Requested portlet mode " + request.getPortletMode());
if(request.getPortletMode().equals(configMode)){
System.out.println("Request for config mode");
doConfig(request, response);
}else if(request.getPortletMode().equals(editDefaultsMode)){
System.out.println("Request for edit_defaults mode");
doEditDefaults(request, response);
}else{
super.doDispatch(request, response);
}

System.out.println("Exiting CustomPortletMode.doDispatch");
}
}


The WPSCustomPortletMode.java overrides doDispatch() and forwards control to corresponding custom modes. I am forwarding control to same index.jsp in edit, edit_defaults and config mode and all three of them allow user to submit a value for userName that i am storing in preference in processAction() method. Depending on the mode this value will get stored at different preference level.

This is how the portlet.xml file for WPCustomPortletMode looks like

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>WPSCustomPortletMode</portlet-name>
<display-name>WPS Custom Portlet Mode Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.WPSCustomPortletMode</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
<portlet-mode>config</portlet-mode>
<portlet-mode>edit_defaults</portlet-mode>
</supports>
<portlet-info>
<title>WPS Custom Portlet Mode Portlet</title>
<short-title>WPS Custom Portlet Mode Portlet</short-title>
<keywords>WPS Custom Portlet Mode Portlet</keywords>
</portlet-info>
</portlet>
<custom-portlet-mode>
<description>Shared Settings mode</description>
<portlet-mode>edit_defaults</portlet-mode>
</custom-portlet-mode>
<custom-portlet-mode>
<description>Administrative mode</description>
<portlet-mode>config</portlet-mode>
</custom-portlet-mode>
</portlet-app>


I am defining two custom-portlet-modes here and for both of them the value of portal-managed equal to true, which is default value to indicate that these custom portlet modes are managed by WebSphere Portal

Non portal managed custom portlet modes

Portal vendors may define custom portlet modes for vendor specific functionality for modes that need to be managed by the portal. Portlets may define additional modes that don’t need to be managed by the portal and correspond to the VIEW mode from a portal point of view. The portlet must declare portlet modes that are not managed by the portal via the <portal-managed>false</portal-managed> tag. Portlet modes are considered portal managed by default.

I tried building a sample portlet, to demonstrate if i can use a non container managed portlet mode. My sample portlet has a clipboard mode and in that mode i am just displaying markup that says this clipbarod mode.


<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>CustomPortletMode</portlet-name>
<display-name>Custom Portlet Mode Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.CustomPortletMode</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>clipboard</portlet-mode>

</supports>
<portlet-info>
<title>Custom Portlet Mode Portlet</title>
<short-title>Custom Portlet Mode Portlet</short-title>
<keywords>Custom Portlet Mode Portlet</keywords>
</portlet-info>
</portlet>
<custom-portlet-mode>
<description>Sample non portal managed mode</description>
<portlet-mode>clipboard</portlet-mode>
</custom-portlet-mode>

</portlet-app>



This is how my portlet.xml file looks like. I did add one custom-portlet-mode declaration for clipboard with value of portal-managed equal to false indicating that this mode is not managed by portal server.

Then i did create a CustomPortletMode.java like this

package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletURL;
import javax.portlet.RenderMode;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class CustomPortletMode extends GenericPortlet{

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.doView()");
response.setContentType("text/html");
response.getWriter().println("Hello from View Mode");
/* PortletURL porteltURL = response.createRenderURL();
porteltURL.setPortletMode(new PortletMode("clipboard"));
response.getWriter().println("<a href='"+ porteltURL.toString()+"'>Go to clipboard Mode</a>"); */
System.out.println("Exiting CustomPortletMode.doView()");
}


protected void doClipboard(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
System.out.println("Entering CustomPortletMode.doClipboard()");
response.setContentType("text/html");
response.getWriter().println("Hello from Clipboard Mode");
System.out.println("Exiting CustomPortletMode.doClipboard()");

}


PortletMode clipboardMode = new PortletMode("clipboard");
protected void doDispatch(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering CustomPortletMode.doDispatch");
System.out.println("Portlet Mode " + request.getPortletMode());
System.out.println("Is Config mode " + request.getPortletMode().equals(configMode));
System.out.println("Is Config mode " + request.getPortletMode().equals(clipboardMode));
if(request.getPortletMode().equals(clipboardMode)){
doClipboard(request, response);
}else{
super.doDispatch(request, response);
}
System.out.println("Exiting CustomPortletMode.doDispatch");
}

}


When i tried deploying this portlet in the WebSPhere Portal Server it did not show me a button to switch to Clipboard mode. I tried creating a link in the VIEW mode manually that will give me change to move to clipboard mode. But when i deployed in WPS the doView() started failing with this exception. It seems that WPS does not like concept of non portal managed custom mode.

If you want to use a Custom POrtlet mode in your portlet, then you will have to override the doDispatch() method of the GenericPortlet class and in that method check if the request is for custom portlet mode and if yes forward control to the appropriate method to handle that mode, if you dont do that GenericPortlet will throw exception



javax.portlet.PortletModeException: Can't set this PortletMode
at com.ibm.ws.portletcontainer.core.impl.PortletURLImpl.setPortletMode(PortletURLImpl.java:74)
at com.webspherenotes.portlet.jsr286.CustomPortletMode.doView(CustomPortletMode.java:20)
at javax.portlet.GenericPortlet.doDispatch(GenericPortlet.java:328)
at javax.portlet.GenericPortlet.render(GenericPortlet.java:233)
at com.ibm.ws.portletcontainer.invoker.impl.PortletFilterChainImpl.doFilter(PortletFilterChainImpl.java:128)
at com.ibm.wps.propertybroker.standard.filter.C2APortletFilter.doFilter(C2APortletFilter.java:183)
at com.ibm.ws.portletcontainer.invoker.impl.PortletFilterChainImpl.doFilter(PortletFilterChainImpl.java:120)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServlet.doDispatch(PortletServlet.java:573)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:114)
at com.ibm.isclite.container.collaborator.PortletServletCollaborator.doRender(PortletServletCollaborator.java:68)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:105)
at com.ibm.ws.portletcontainer.rrd.RRDServerPortletServletCollaborator.doRender(RRDServerPortletServletCollaborator.java:123)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:105)
at com.ibm.ws.portletcontainer.cache.CacheCollaborator.doRender(CacheCollaborator.java:92)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:105)
at com.ibm.wps.pe.pc.waspc.core.impl.PortletServletCollaboratorImpl.doRender(PortletServletCollaboratorImpl.java:156)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:105)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServlet.doDispatch(PortletServlet.java:273)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:82)
at com.ibm.isclite.container.collaborator.PortletServletCollaborator.doDispatch(PortletServletCollaborator.java:124)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:74)
at com.ibm.ws.portletcontainer.rrd.RRDServerPortletServletCollaborator.doDispatch(RRDServerPortletServletCollaborator.java:60)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:74)
at com.ibm.ws.portletcontainer.cache.CacheCollaborator.doDispatch(CacheCollaborator.java:74)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:74)
at com.ibm.wps.pe.pc.waspc.core.impl.PortletServletCollaboratorImpl.doDispatch(PortletServletCollaboratorImpl.java:121)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServletCollaboratorChainImpl.doCollaborator(PortletServletCollaboratorChainImpl.java:74)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServlet.dispatch(PortletServlet.java:208)
at com.ibm.ws.portletcontainer.invoker.impl.PortletServlet.service(PortletServlet.java:165)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1146)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1087)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:118)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:837)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:680)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:588)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:524)
at com.ibm.wsspi.webcontainer.servlet.GenericServletWrapper.handleRequest(GenericServletWrapper.java:122)
at com.ibm.ws.portletcontainer.webextension.PortletExtensionProcessor.handleRequest(PortletExtensionProcessor.java:93)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.include(WebAppRequestDispatcher.java:639)
at com.ibm.ws.portletcontainer.invoker.impl.PortletInvokerImpl.invoke(PortletInvokerImpl.java:235)
at com.ibm.ws.portletcontainer.invoker.impl.PortletInvokerCollaboratorChainImpl.doCollaborator(PortletInvokerCollaboratorChainImpl.java:78)
at com.ibm.ws.portletcontainer.cache.PortletInvokerCacheCollaborator.doRender(PortletInvokerCacheCollaborator.java:58)
at com.ibm.ws.portletcontainer.invoker.impl.PortletInvokerCollaboratorChainImpl.doCollaborator(PortletInvokerCollaboratorChainImpl.java:67)
at com.ibm.ws.portletcontainer.ext.PortletInvokerPerformanceCollaborator.invoke(PortletInvokerPerformanceCollaborator.java:313)
at com.ibm.ws.portletcontainer.ext.PortletInvokerPerformanceCollaborator.doInvoke(PortletInvokerPerformanceCollaborator.java:101)
at com.ibm.ws.portletcontainer.ext.PortletInvokerPerformanceCollaborator.invokePMI(PortletInvokerPerformanceCollaborator.java:163)
at com.ibm.ws.portletcontainer.ext.PortletInvokerPerformanceCollaborator.doInvoke(PortletInvokerPerformanceCollaborator.java:91)
at com.ibm.ws.portletcontainer.ext.PortletInvokerPerformanceCollaborator.doRender(PortletInvokerPerformanceCollaborator.java:74)
at com.ibm.ws.portletcontainer.invoker.impl.PortletInvokerCollaboratorChainImpl.doCollaborator(PortletInvokerCollaboratorChainImpl.java:67)
at com.ibm.ws.portletcontainer.invoker.impl.PortletInvokerImpl.render(PortletInvokerImpl.java:97)
at com.ibm.ws.portletcontainer.PortletContainerImpl.doRender(PortletContainerImpl.java:119)


When i deployed same code in the Apache Pluto i could go to Clipboard button like this

Wild cards in process supported-processing-event or supported-publishing events

In the Patterns in ProcessEvent annotation entry i talked about how you can use . as wild card character while annotating methods that should be used for handling the event. But the concept of wild card also applies to the supported-processing-event or supported-publishing-event. This is what the Portlet Specification says

"The portlet is encouraged to organize the local part of the event names in the event-definition element in a hierarchical manner using the dot ‘.’ as separator. A trailing '.'tells the Consumer that this is not the end of the hierarchy and the Portlet is interested in all events with names in this branch of the hierarchy. The portlet must not specify events with the same name but different types. Event names in the event-definition element hould not end with a trailing “.” character as wildcards are not supported in the event 0 definition level. Wildcards should only be used in the supported-processing-event or supported-publishing-event elements and should be able to be resolved by the portlet container to an event definition without wildcards in the event-definition element by matching event names ending with a "." character to any event whose local name starts with the characters before the "." character and also specifies the same namespace. If the wildcard string should match a part of a hierarchy two dots are required at the end of the wildcard string: one to denote the hierarchy and one for the wildcard: “foo.bar..”."

Basic idea is while declaring event you will have to specify fully qualified name of the event and you should use a hierarchical name such as say com.webspherenotes.events.contact, com.webspherenotes.events.address, com.webspherenotes.events.phone But while declaring which portlets can publish or subscribe the event you can use wild character like this com.webspherenotes.events..

Take a look at this sample portlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>ProcessEventAnnotationPortlet</portlet-name>
<display-name>Process Event Annotation Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.ProcessEventAnnotationPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
</supports>
<portlet-info>
<title>Process Event Annotation Portlet</title>
<short-title>Process Event Annotation Portlet</short-title>
<keywords>Process Event Annotation Portlet</keywords>
</portlet-info>
<supported-processing-event>
<name>com.webspherenotes.events.</name>
</supported-processing-event>
<supported-publishing-event>
<name>com.webspherenotes..</name>
</supported-publishing-event>

</portlet>

<default-namespace>http://wpcertification.blogspot.com</default-namespace>
<event-definition>
<name>com.webspherenotes.events.contact</name>
<value-type>com.webspherenotes.portlet.events.Contact</value-type>
</event-definition>
<event-definition>
<name>com.webspherenotes.events.address</name>
<value-type>com.webspherenotes.portlet.events.Address</value-type>
</event-definition>
<event-definition>
<name>com.webspherenotes.events.phone</name>
<value-type>com.webspherenotes.portlet.events.Phone</value-type>
</event-definition>

</portlet-app>


The ProcessEventAnnotationPortlet can process all events starting with com.webspherenotes.events but it can publish all the events starting with com.webspherenotes

Patterns in ProcessEvent annotation

In the ProcessEvent annotation entry i built a sample portlet to demonstrate how we can use @ProcessEvent annotation.

When the processEvent() method in GenericPortlet gets invoked, first it checks if there there is exact match of event name and the value of name attribute of @ProcessEvent annotation. If it does not find exact match it tries to find the longest possible match using following rule

"If the local part of the event name has a wildcard at the end (“.”) the GenericPortlet will try to match the received event either to the same wildcard event name or to the longest matching event name for this wildcard. E.g. if an event with the local part of the event name of "a.b.c.d" is being received and there are methods annotated for handling "a.b." and "a.b.c." events in this portlet, the GenericPortlet will dispatch the event to the method annotated with "a.b.c."

Lets say this is how portelt.xml of my target portlet looks like


<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>ProcessEventAnnotationPortlet</portlet-name>
<display-name>Process Event Annotation Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.ProcessEventAnnotationPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
</supports>
<portlet-info>
<title>Process Event Annotation Portlet</title>
<short-title>Process Event Annotation Portlet</short-title>
<keywords>Process Event Annotation Portlet</keywords>
</portlet-info>
<supported-processing-event>
<name>com.webspherenotes.events.contact</name>
</supported-processing-event>
</portlet>
<default-namespace>http://wpcertification.blogspot.com</default-namespace>
<event-definition>
<name>com.webspherenotes.events.contact</name>
<value-type>com.webspherenotes.portlet.events.Contact</value-type>
</event-definition>


</portlet-app>


The ProcessEventAnnotationPortlet can consume com.webspherenotes.events.contact event. So these are some of the @ProcessEvent combination that i can use to handle this event


  1. @ProcessEvent(qname="{http://wpcertification.blogspot.com}com.webspherenotes.events.contact"): Fully qualified event name

  2. @ProcessEvent(qname="{http://wpcertification.blogspot.com}com.webspherenotes."): Fully qualified event name, so this method can handle all the events starting with com.webspherenotes.*, it could be com.webspherenotes.events.contact,com.webspherenotes.events.hello

  3. @ProcessEvent(name = "com.webspherenotes."): Means it can handle all the events where local name starts with com.webspherenotes.*

ProcessEvent annotation

This is how the default implementation of the processEvent() method in GenricPortlet looks like,

public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException {
String eventName = request.getEvent().getQName().toString();
try {
// check for exact match
Method eventMethod = processEventHandlingMethodsMap.get(eventName);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
} else {
// Search for the longest possible matching wildcard annotation
int endPos = eventName.indexOf('}');
int dotPos = eventName.lastIndexOf('.');
while (dotPos > endPos) {
String wildcardLookup = eventName.substring(0, dotPos + 1);
eventMethod = processEventHandlingMethodsMap.get(wildcardLookup);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
}
if (dotPos == 0) {
break;
}
dotPos = eventName.lastIndexOf('.', dotPos - 1);
}
}
} catch (Exception e) {
throw new PortletException(e);
}
// if no event processing method was found just keep render params
response.setRenderParameters(request);
}


First it tries to figure out if there is a method with @ProcessEvent annotation, that matches current event, if yes it forwards control to that method if not it sets the current render parameters as new render parameters.

You can annotate a method use ProcessEvent annotation using two different approaches

  • Using QName: You can specify the fully qualified name of the event using "{" + Namespace URI + "}" + local part format. Ex. @ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")

  • Using local Name:For using only the local part of the event name and leverage the default namespace defined in the portlet deployment descriptor with the default-namespace element the following alternative is provided: @ProcessEvent (name=), where the event name is only the local part.



You can download the sample portlet that i built to demonstrate how to use @ProcessEvent from here


package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.Event;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.ProcessEvent;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import com.webspherenotes.portlet.events.Contact;

public class ProcessEventAnnotationPortlet extends GenericPortlet {

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.doView()");
response.setContentType("text/html");
Contact contact = (Contact) request.getPortletSession().getAttribute(
"contact");
if (contact != null) {
response.getWriter().println(
"First Name " + contact.getFirstName() + "
Last Name "
+ contact.getLastName() + "
Email "
+ contact.getEmail());
} else {
response.getWriter().println("Contact not found in session ");
}
System.out.println("Exiting ProcessActionAnnotationPortlet.doView()");
}

//@ProcessEvent(name = "hello")
@ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")
public void handleContactEvent(EventRequest request, EventResponse response)
throws PortletException, IOException {
System.out
.println("Entering ProcessActionAnnotationPortlet.handleContactEvent()");
Event event = request.getEvent();
System.out.println("Event Name " + event.getName());
System.out.println("Event Value " + event.getValue());
Contact contact = (Contact) event.getValue();
System.out.println("Contact First Name " + contact.getFirstName());
System.out.println("Contact Last Name " + contact.getLastName());
System.out.println("Contact Email " + contact.getEmail());
request.getPortletSession().setAttribute("contact", contact);
System.out
.println("Entering ProcessActionAnnotationPortlet.handleContactEvent()");
}

}


The ProcessEventAnnotationPortlet can act as target of hello event. It has a handleContactEvent method that can be used for handling the hello event. I can annotate it using either only local name like this @ProcessEvent(name = "hello") or using fully qualified name like this @ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")

This is the portlet.xml for my Sample portlet

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>ProcessEventAnnotationPortlet</portlet-name>
<display-name>Process Event Annotation Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.ProcessEventAnnotationPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
</supports>
<portlet-info>
<title>Process Event Annotation Portlet</title>
<short-title>Process Event Annotation Portlet</short-title>
<keywords>Process Event Annotation Portlet</keywords>
</portlet-info>
<supported-processing-event>
<name>hello</name>
</supported-processing-event>

</portlet>
<default-namespace>http://wpcertification.blogspot.com</default-namespace>
<event-definition>
<name>hello</name>
<value-type>com.webspherenotes.portlet.events.Contact</value-type>
</event-definition>

</portlet-app>

RenderMode annotation

The doDispatch method of GenericPortlet class is modified in the Portlet Specification 2.0, so that now when it gets control first it checks if there is any method in your Portlet class which has @RenderMode annotation that matches the portlet mode of incoming request, if yes it forwards control to that method, if not then the doDispatch method will forward control to following methods

  • doView() for handling VIEW mode

  • doEdit() for handling EDIT mode

  • doHelp() for handling HELP mode



This is how the doDispatch() method in GenericPortlet class looks like

protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException,
java.io.IOException {
WindowState state = request.getWindowState();
if (!state.equals(WindowState.MINIMIZED)) {
PortletMode mode = request.getPortletMode();
// first look if there are methods annotated for
// handling the rendering of this mode
try {
// check if mode is cached
Method renderMethod = renderModeHandlingMethodsMap.get(mode.toString());
if (renderMethod != null) {
renderMethod.invoke(this, request, response);
return;
}
} catch (Exception e) {
throw new PortletException(e);
}

// if not, try the default doXYZ methods
if (mode.equals(PortletMode.VIEW)) {
doView(request, response);
} else if (mode.equals(PortletMode.EDIT)) {
doEdit(request, response);
} else if (mode.equals(PortletMode.HELP)) {
doHelp(request, response);
} else {
throw new PortletException("unknown portlet mode: " + mode);
}
}


I wanted to see how @RenderMode annotation works so i built a sample portlet like this, you can download it from here


package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderMode;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class RenderModeAnnotationPortlet extends GenericPortlet{

@RenderMode(name="view")
public void handleViewMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleViewMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>View Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleViewMode()");
}

@RenderMode(name="edit")
public void handleEditMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleEditMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>Edit Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleEditMode()");
}

@RenderMode(name="help")
public void handleHelpMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleHelpMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>Help Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleHelpMode()");
}
}


If you want to use @RenderMode annotation, then you will have to do two things, first create a method with following signature


void (RenderRequest, RenderResponse) throws
PortletException, java.io.IOException;


Mark that method with @RenderMode(name="<modename>") attribute where value of name equals to name of the PortletMode that this method should handle

ProcessAction annotation

The JSR 286 interface introduces concept of ProcessAction annotation that you can use for marking a method that can be used for handling particular action. I built a simple portlet to demonstrate how you can use @ProcessAction annotation, you can download the sample portlet from here

This is how my ProcessActionAnnotationPortlet.java looks like

package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.ProcessAction;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class ProcessActionAnnotationPortlet extends GenericPortlet{

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.doView()");
response.setContentType("text/html");

getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting ProcessActionAnnotationPortlet.doView()");
}

@ProcessAction(name="action1")
public void handleAction1(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction1()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action1");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction1()");
}

@ProcessAction(name="action2")
public void handleAction2(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction2()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action2");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction2()");
}

@ProcessAction(name="action3")
public void handleAction3(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction3()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action3");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction3()");
}

}


As you can see i have three methods that are marked with @ProcessAction annotation, the handleAction1, is marked with @ProcessAction(name="action1"), which means that whenever there is processAction call with value of javax.portlet.action parameter equal to action1, it will call handleAction1 method.

The Annotations are implemented using GenericPortlet For a received action the processAction method in the GenericPortlet class tries to dispatch to methods annotated with the tag @ProcessAction(name=<action name>), where the action name must be set on the ActionURL as value of the parameter javax.portlet.action (or via the constant ActionRequest.ACTION_NAME), and following signature:

void <methodname> (ActionRequest, ActionResponse) throws
PortletException, java.io.IOException;

A portlet that wants to leverage this action dispatching needs to set the parameter ActionRequest.ACTION_NAME on the action URL.


This is how my JSP looks like

<%@page language="java" contentType="text/html; %>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<portlet:defineObjects />
Current Action - <%=renderRequest.getParameter("action") %> <br/>
<portlet:actionURL var="action1Url">
<portlet:param name="javax.portlet.action" value="action1" />
</portlet:actionURL>
<a href='<%=action1Url %>' >Action1 URl</a><br/>

<portlet:actionURL var="action2Url">
<portlet:param name="javax.portlet.action" value="action2" />
</portlet:actionURL>
<a href='<%=action2Url %>' >Action2 URl</a><br/>

<portlet:actionURL var="action3Url">
<portlet:param name="javax.portlet.action" value="action3" />
</portlet:actionURL>
<a href='<%=action3Url %>' >Action3 URl</a><br/>

Passing Complex Events in WSRP

In the Passing complex objects in portlet events entry i talked about how you can pass complex/custom objects as event payload.

You can download the sample code for this project from here

  1. Contact

  2. EventSource

  3. EventTarget



But you will have to make some additional changes to your portlet if you want to pass these object between portlets installed on different severs using WSRP. In case when objects are hosted on different server then portal server will serialize the complex object using JAXB and then send it to consumer. I changed my existing Contact object like this to make it work in WSRP


package com.webspherenotes.portlet.events;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Contact implements Serializable{
private static final long serialVersionUID = -1637774642655976822L;
private String firstName;
private String lastName;
private String email;
public Contact(){
}
public Contact(String firstName, String lastName, String email) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}


I had to annotate the Contact object by adding @XmlRootElement this. You will have to copy the contact.jar in shared library of both producer and consumer. Also make sure that Contact class has no-arg constructor.


When my Contact.java class did not had constructor i was getting error like this.


[5/19/10 9:48:13:456 PDT] 00000071 PropertyDispa E com.ibm.wps.propertybroker.dispatch.PropertyDispatcherImpl dispatchSourceEvents EJPKB1002E: The propertybroker encountered an error while dispatching the source event {http://wpcertification.blogspot.com}hello from portlet window Control@1598316356 (SourcePortlet, [ObjectIDImpl '7_VVILMKG1009800I2DO701500G7', NAVIGATION_NODE, VP: 0, [Domain: rel], DB: 0000-FFCB6A290C00240480140D1F100A00F0], [ObjectIDImpl '6_VVILMKG10G4K30I2A4E1UM2007', CONTENT_NODE, VP: 0, [Domain: rel], DB: 0000-FFCB6A290C00123A80148AB8E0AD00E0], 100 with value com.webspherenotes.portlet.events.Contact@212c212c.
com.ibm.portal.propertybroker.exceptions.CommunicationTargetDispatchException: EJPKB1001E: An Error occurred while dispatching to the communication target

process.{http://wpcertification.blogspot.com}hello
11_VVILMKG1009800I2DO701500O3

{http://wpcertification.blogspot.com}hello
com.ibm.wps.pe.pc.util.JAXBEventPayloadWrapper
{http://wpcertification.blogspot.com}hello


for window Control@1605918648 (TargetPortlet, [ObjectIDImpl '7_VVILMKG1009800I2DO70150040', NAVIGATION_NODE, VP: 0, [Domain: rel], DB: 0000-FFCB6A290C00240480140D1F100A0004], [ObjectIDImpl '6_VVILMKG10G4K30I2A4E1UM2007', CONTENT_NODE, VP: 0, [Domain: rel], DB: 0000-FFCB6A290C00123A80148AB8E0AD00E0], 200.
at com.ibm.wps.propertybroker.standard.filter.JsrEventActionDispatcherPluginImpl.buildTargetJsrEventInformation(JsrEventActionDispatcherPluginImpl.java:263)
at com.ibm.wps.propertybroker.standard.filter.JsrEventActionDispatcherPluginImpl.dispatchCommunicationTarget(JsrEventActionDispatcherPluginImpl.java:106)
at com.ibm.wps.propertybroker.dispatch.PropertyDispatcherImpl.dispatchCommunicationTarget(PropertyDispatcherImpl.java:658)
at com.ibm.wps.propertybroker.dispatch.PropertyDispatcherImpl.dispatchCommunicationTargets(PropertyDispatcherImpl.java:611)
at com.ibm.wps.propertybroker.dispatch.PropertyDispatcherImpl.dispatchPropertyValues(PropertyDispatcherImpl.java:256)
at com.ibm.wps.propertybroker.dispatch.PropertyDispatcherImpl.dispatchSourceEvent(PropertyDispatcherImpl.java:138)
at com.ibm.wps.pe.pc.waspc.services.information.WaspcInformationProviderImpl$PortletEventProviderImpl.add(WaspcInformationProviderImpl.java:373)
at com.ibm.ws.portletcontainer.PortletContainerImpl.publishEventsToEventProvider(PortletContainerImpl.java:431)
at com.ibm.ws.portletcontainer.PortletContainerImpl.doAction(PortletContainerImpl.java:215)
at com.ibm.ws.portletcontainer.PortletContainerInvokerCollaboratorChainImpl.doCollaborator(PortletContainerInvokerCollaboratorChainImpl.java:78)
at com.ibm.ws.portletcontainer.ext.ExtCollaborator.doAction(ExtCollaborator.java:55)
at com.ibm.ws.portletcontainer.PortletContainerInvokerCollaboratorChainImpl.doCollaborator(PortletContainerInvokerCollaboratorChainImpl.java:65)
at com.ibm.ws.portletcontainer.cache.CacheInvokerCollaborator.doAction(CacheInvokerCollaborator.java:76)
at com.ibm.ws.portletcontainer.PortletContainerInvokerCollaboratorChainImpl.doCollaborator(PortletContainerInvokerCollaboratorChainImpl.java:65)
at com.ibm.ws.portletcontainer.PortletContainerImpl.processPortletAction(PortletContainerImpl.java:152)
at com.ibm.ws.portletcontainer.pcinvoker.PortletInvokerImpl$1.run(PortletInvokerImpl.java:59)
at java.security.AccessController.doPrivileged(AccessController.java:246)
at com.ibm.ws.portletcontainer.pcinvoker.PortletInvokerImpl.invokeProcessAction(PortletInvokerImpl.java:55)
at com.ibm.wps.pe.pc.waspc.core.impl.PortletInvokerImpl$3.invoke(PortletInvokerImpl.java:115)
at com.ibm.wps.pe.pc.waspc.core.impl.PortletInvokerImpl.invoke(PortletInvokerImpl.java:175)
at com.ibm.wps.pe.pc.waspc.core.impl.PortletInvokerImpl.invokeProcessAction(PortletInvokerImpl.java:113)
at com.ibm.wps.pe.pc.waspc.event.ActionEvent.execute(ActionEvent.java:78)
at com.ibm.wps.pe.pc.waspc.event.EventQueueManager.processEventLoop(EventQueueManager.java:112)
at com.ibm.wps.pe.pc.waspc.PortletContainerImpl.performEvents(PortletContainerImpl.java:206)
at com.ibm.wps.pe.pc.PortletContainerImpl.performEvents(PortletContainerImpl.java:298)
at com.ibm.wps.engine.phases.WPActionPhase.processPortlets(WPActionPhase.java:2644)
at com.ibm.wps.engine.phases.WPActionPhase.execute(WPActionPhase.java:668)
at com.ibm.wps.state.phases.AbstractActionPhase.next(AbstractActionPhase.java:130)
at com.ibm.wps.engine.Servlet.callPortal(Servlet.java:855)
at com.ibm.wps.engine.Servlet.doGet(Servlet.java:617)
at com.ibm.wps.engine.Servlet.doPost(Servlet.java:888)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:763)
at com.ibm.wps.engine.Servlet.doFilter(Servlet.java:1257)
at com.ibm.wps.resolver.servlet.ContentHandlerCleanup.doFilter(ContentHandlerCleanup.java:648)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.engine.Servlet.service(Servlet.java:1248)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1146)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1087)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:145)
at com.ibm.wps.engine.ExtendedLocaleFilter.doFilter(ExtendedLocaleFilter.java:113)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.wps.resolver.friendly.servlet.FriendlySelectionFilter.doFilter(FriendlySelectionFilter.java:191)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.wps.mappingurl.impl.URLAnalyzer.doFilter(URLAnalyzer.java:352)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.wps.engine.VirtualPortalFilter.doFilter(VirtualPortalFilter.java:88)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.wps.state.filter.StateCleanup.doFilter(StateCleanup.java:94)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:837)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:680)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:588)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:524)
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3517)
at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:269)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:818)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1478)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:125)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:267)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java:205)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
com.webspherenotes.portlet.events.Contact does not have a no-arg default constructor.
this problem is related to the following location:
at com.webspherenotes.portlet.events.Contact

at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:66)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:389)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:236)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:76)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:210)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:381)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
at com.ibm.wsspi.portletcontainer.util.EventFactory.serializeJAXB(EventFactory.java:285)
at com.ibm.wsspi.portletcontainer.util.EventFactory.serializeJAXB(EventFactory.java:166)
at com.ibm.wps.propertybroker.standard.filter.JsrEventActionDispatcherPluginImpl.buildTargetJsrEventInformation(JsrEventActionDispatcherPluginImpl.java:226)
... 72 more

Passing Complex Objects using portlet events

In the Hello Portlet Events entry, i built a sample to demonstrate how you can pass simple String using as value of event from one portlet to another. But if you want to pass a custom/ complex object and source and target portlets are not packaged in the same .war file then you will have to follow some additional steps.

I built 2 sample portlets to demonstrate how you can pass complex object from one portlet to another. The EventSourcePortlet passes object of com.webspherenotes.portlet.events.Contact type to TargetSourcePortlet.

You can download the sample code for this project from here

  1. Contact

  2. EventSource

  3. EventTarget



I had to follow these steps


  1. I started by creating Contact Java project, this project has Contact.java class like this

    public class Contact implements Serializable{
    private static final long serialVersionUID = -1637774642655976822L;
    private String firstName;
    private String lastName;
    private String email;
    public Contact(){
    }
    public Contact(String firstName, String lastName, String email) {
    super();
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
    }
    public String getFirstName() {
    return firstName;
    }
    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }
    public String getLastName() {
    return lastName;
    }
    public void setLastName(String lastName) {
    this.lastName = lastName;
    }
    public String getEmail() {
    return email;
    }
    public void setEmail(String email) {
    this.email = email;
    }
    }

    The Contact class is simple POJO class and it implements Serializable interface. That is because the setEvent() method takes object of Serializable type as argument for event value

  2. Compile the Contact java source into contact.jar and copy it into shared library of your portal. In case of WebSphere portal it will be WebSphere/PortalServer/shared/app

  3. Create EventSourcePortlet project and in that create EventSourcePortlet.java like this

    public class EventSourcePortlet extends javax.portlet.GenericPortlet {
    public void init() throws PortletException {
    super.init();
    }
    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering SourcePortlet.doView()");
    response.setContentType("text/html");
    if (request.getPortletSession().getAttribute("contact") != null) {
    request.setAttribute("contact", request.getPortletSession()
    .getAttribute("contact"));
    }
    getPortletContext().getRequestDispatcher("/source.jsp").include(
    request, response);
    System.out.println("Exiting SourcePortlet.doView()");
    }
    public void processAction(ActionRequest request, ActionResponse response)
    throws PortletException, IOException {
    System.out.println("Entering SourcePortlet.processAction()");
    String fName = request.getParameter("fName");
    String lName = request.getParameter("lName");
    String email = request.getParameter("email");
    Contact contact = new Contact(fName, lName, email);
    response.setEvent("hello", contact);
    request.getPortletSession().setAttribute("contact", contact);
    System.out.println("Exiting SourcePortlet.processAction()");
    }
    }

    The doView() method of the EventSourcePortlet is forwarding control to contact.jsp for rendering. The contact.jsp displays a simple form to user where user can input firstName, lastName and email.

    The processAction() method of EventSourcePortlet gets control when user enters values on the form and clicks submit. In this method, I am reading values submitted by user and then creating a Contact object and then setting and event with name equal to hello and value equal to the Contact object.


  4. Create EventTargetPortlet and inside that create EventTargetPortlet.java like this

    public class EventTargetPortlet extends javax.portlet.GenericPortlet {
    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering TargetPortlet.doView()");
    response.setContentType("text/html");
    Contact contact = (Contact) request.getPortletSession().getAttribute(
    "contact");
    if (contact != null) {
    response.getWriter().println(
    "First Name " + contact.getFirstName() + "
    Last Name "
    + contact.getLastName() + "
    Email "
    + contact.getEmail());
    } else {
    response.getWriter().println("Contact not found in session ");
    }
    System.out.println("Exiting TargetPortlet.doView()");
    }

    public void processEvent(EventRequest request, EventResponse response)
    throws PortletException, IOException {
    System.out.println("Entering TargetPortlet.processEvent");
    Event event = request.getEvent();
    System.out.println("Event Name " + event.getName());
    Contact contact = (Contact) event.getValue();
    System.out.println("Contact First Name " + contact.getFirstName());
    System.out.println("Contact Last Name " + contact.getLastName());
    System.out.println("Contact Email " + contact.getEmail());
    request.getPortletSession().setAttribute("contact", contact);
    System.out.println("Exiting TargetPortlet.processEvent");
    }
    }

    The processEvent() method of the EventTargetPortlet get control whenever it receives event, in this method it is reading the Contact object that was sent as event payload and then setting it as attribute in PortletSession.
    In the doView() method it reads the Contact object from PortletSession and prints those values

  5. This is how the portlet.xml file for the EventSourcePortlet looks like

    <?xml version="1.0" encoding="UTF-8"?>
    <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
    id="com.webspherenotes.portlet.event.EventSourcePortlet.1a884b6b82">
    <portlet>
    <portlet-name>EventSourcePortlet</portlet-name>
    <display-name xml:lang="en">EventSourcePortlet</display-name>
    <display-name>EventSourcePortlet</display-name>
    <portlet-class>com.webspherenotes.portlet.event.EventSourcePortlet</portlet-class>
    <init-param>
    <name>wps.markup</name>
    <value>html</value>
    </init-param>
    <expiration-cache>0</expiration-cache>
    <supports>
    <mime-type>text/html</mime-type>
    <portlet-mode>view</portlet-mode>
    </supports>
    <supported-locale>en</supported-locale>
    <resource-bundle>com.webspherenotes.portlet.event.nl.EventSourcePortletResource</resource-bundle>
    <portlet-info>
    <title>EventSourcePortlet</title>
    <short-title>EventSourcePortlet</short-title>
    <keywords>EventSourcePortlet</keywords>
    </portlet-info>
    <supported-publishing-event>
    <name>hello</name>
    </supported-publishing-event>
    </portlet>
    <default-namespace>http://wpcertification.blogspot.com</default-namespace>
    <event-definition>
    <name>hello</name>
    <value-type>com.webspherenotes.portlet.events.Contact</value-type>
    </event-definition>
    </portlet-app>

    As you can see the value-type of the event is com.webspherenotes.portlet.events.Contact to indicate that we want to send object of Contact class as event payload

Hello Portlet Events

The Portlet Specification 2.0 has concept of Events, that can be used for portlet to portlet communication. I built this sample HelloEventsPortlet to demonstrate how to create simple portlet events communication

The HelloEventsPortlet application has two portlets SourcePortlet, which will display Action URL to the user in VIEW mode and when user clicks on the ActionURL it will set hello event in the processAction method, the TargetPortlet will act as consumer for the event and in the processEvent method it will read value of the event and set it as render parameter and display that value to the user in VIEW mode markup. Follow these steps to create events portlet


  • First create SourcePortlet.java like this


    package com.webspherenotes.portlet.events;

    import java.io.IOException;

    import javax.portlet.ActionRequest;
    import javax.portlet.ActionResponse;
    import javax.portlet.GenericPortlet;
    import javax.portlet.PortletException;
    import javax.portlet.RenderRequest;
    import javax.portlet.RenderResponse;

    public class SourcePortlet extends GenericPortlet{

    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering SourcePortlet.doView()");
    response.setContentType("text/html");
    getPortletContext().getRequestDispatcher("/source.jsp").include(request, response);
    System.out.println("Exiting SourcePortlet.doView()");
    }

    public void processAction(ActionRequest request, ActionResponse response)
    throws PortletException, IOException {
    System.out.println("Entering SourcePortlet.processAction()");
    response.setEvent("hello", "Hello event from the SourcePortlet");
    System.out.println("Exiting SourcePortlet.processAction()");
    }

    }

    The SourcePortlet has doView() and processAction() methods, in doView() method it is forwarding control to source.jsp for generating markup, the source.jsp generates a simple ActionURL.
    In the processAction() method it is calling response.setEvent("hello", "Hello event from the SourcePortlet"), which is used for publishing hello event.


  • This is how my TargetPortlet.java looks like

    package com.webspherenotes.portlet.events;

    import java.io.IOException;

    import javax.portlet.Event;
    import javax.portlet.EventPortlet;
    import javax.portlet.EventRequest;
    import javax.portlet.EventResponse;
    import javax.portlet.GenericPortlet;
    import javax.portlet.PortletException;
    import javax.portlet.RenderRequest;
    import javax.portlet.RenderResponse;

    public class TargetPortlet extends GenericPortlet implements EventPortlet{

    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering TargetPortlet.doView()");
    response.setContentType("text/html");
    response.getWriter().println("Hello from TargetPortlet.doView() " + request.getParameter("helloEvent"));
    System.out.println("Exiting TargetPortlet.doView()");
    }

    public void processEvent(EventRequest request, EventResponse response)
    throws PortletException, IOException {
    System.out.println("Entering TargetPortlet.processEvent");
    Event event= request.getEvent();
    System.out.println("Event Name " + event.getName());
    String eventValue =(String)event.getValue();
    System.out.println("Event Value " + eventValue);
    response.setRenderParameter("helloEvent", eventValue);

    System.out.println("Exiting TargetPortlet.processEvent");
    }

    }

    The TargetPortlet is implementing EventPortlet to indicate that it can act as consumer of event. The TargetPortlet has doView() and processEvent() methods, in doView() it is simply reading value of helloEvent render parameter and displaying it to user.

    The portlet container will call processEvent() method to give TargetPortlet chance to consume the event, it will get called before the render phase. You can call EventRequest.getEvent()method inside the processEvent() method to get the Event object and once you have the event object you can call its getName() and getValue() methods to read name and value of the event. After reading the value of event i am setting it as render parameter for the portlet.


  • This is how my portlet.xml file looks like

    <?xml version="1.0" encoding="UTF-8"?>
    <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
    version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
    <portlet>
    <portlet-name>SourcePortlet</portlet-name>
    <display-name>Events Source Portlet</display-name>
    <portlet-class>com.webspherenotes.portlet.events.SourcePortlet</portlet-class>
    <expiration-cache>0</expiration-cache>
    <supports>
    <mime-type>text/html</mime-type>
    <portlet-mode>view</portlet-mode>
    <portlet-mode>edit</portlet-mode>
    </supports>
    <portlet-info>
    <title>Event Source Portlet</title>
    <short-title>Event Source Portlet</short-title>
    <keywords>Event Source Portlet</keywords>
    </portlet-info>
    <supported-publishing-event>
    <name>hello</name>
    </supported-publishing-event>

    </portlet>
    <portlet>
    <portlet-name>TargetPortlet</portlet-name>
    <display-name>Events Target Portlet</display-name>
    <portlet-class>com.webspherenotes.portlet.events.TargetPortlet</portlet-class>
    <expiration-cache>0</expiration-cache>
    <supports>
    <mime-type>text/html</mime-type>
    <portlet-mode>view</portlet-mode>
    <portlet-mode>edit</portlet-mode>
    </supports>
    <portlet-info>
    <title>Event Target Portlet</title>
    <short-title>Event Target Portlet</short-title>
    <keywords>Event Target Portlet</keywords>
    </portlet-info>
    <supported-processing-event>
    <name>hello</name>
    </supported-processing-event>

    </portlet>
    <default-namespace>http://wpcertification.blogspot.com</default-namespace>
    <event-definition>
    <name>hello</name>
    <value-type>java.lang.String</value-type>
    </event-definition>

    </portlet-app>

    There are three components in the portlet.xml

    1. event-definition: Element is declared at the portlet application level and it defines name of the event and the type of the event. In my case name of the event is hello and type is java.lang.Style

    2. supported-publishing-event: Element is used for declaring what all events can be published by this portlet

    3. supported-processing-event: Element is used for defining what all events can be consumed by this portlet



  • After developing this portlet deploy it in WebSphere Portal server and add them to a portal page

  • Now create a wire between consumer and producer using Portlet Wiring Portlet like this




Now you can test this portlet by clicking on the Action URL in the Source Portlet, you will notice that the TargetPortlet is displaying the value of event to user

What are Portlet Events

Portlet events are intended to allow portlets to react to actions or state changes not directly related to an interaction of the user with the portlet. Events could be either portal or portlet container generated or the result of a user interaction with other portlets. The portlet event model is a loosely coupled, brokered model that allows creating portlets as stand-alone portlets that can be wired together with other portlets at runtime. In response to an event a portlet may publish new events that should be delivered to other portlets and thus may trigger state changes on these other portlets.

In order to receive events the portlet must implement the EventPortlet interface in the javax.portlet package. The portlet container will call the processEvent method for each event targeted to the portlet with an EventRequest and EventResponse object. Events are targeted by the portal / portlet container to a specific portlet window in the current client request.

Events are a life cycle operation that occurs before the rendering phase. The portlet may issue events via the setEvent method during the action processing which will be processed by the portlet container after the action processing has finished. As a result of issuing an event the portlet may optionally receive events from other portlets or container events. A portlet that is not target of a user action may optionally receive container events, e.g. a portlet mode changed event, or events from other portlets, e.g. an item was added to the shopping cart event.

Getting name of the page where your portlet is getting rendered

Knowing name of the page where your portlet is getting rendered is one of the very common requirement, i know as per portlet specification your portlet should not be aware about where it is getting rendered but in reality there are requirements that portlet needs to behave differently based on the position where it is getting rendered.

The WebSphere Portal Server has concept of NavigationSelectionModel SPI that you can use to know about the current page.This SPI is used by the theme to know the page which should be displayed to the user. I built a PageNameFilter that makes use of the NavigationSelectionModel to find out where the page is getting rendered and passing it to the portlet as value of com.webspherenotes.filter.pageName request attribute, so that it works on both local portlet as well as cases when the portlet is getting consumed as WSRP (in case of WSRP, it will give you name of the page where portlet is getting consumed)


package com.webspherenotes.portlet.filter;

import java.io.IOException;
import java.util.Locale;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.filter.FilterChain;
import javax.portlet.filter.FilterConfig;
import javax.portlet.filter.RenderFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ibm.portal.ModelException;
import com.ibm.portal.content.ContentNode;
import com.ibm.portal.model.NavigationSelectionModelHome;
import com.ibm.portal.model.NavigationSelectionModelProvider;
import com.ibm.portal.navigation.NavigationNode;
import com.ibm.portal.navigation.NavigationSelectionModel;

public class PageNameFilter implements RenderFilter{
private static final Logger logger = LoggerFactory.getLogger(PageNameFilter.class);
public void doFilter(RenderRequest request, RenderResponse response,
FilterChain filterChain) throws IOException, PortletException {
logger.debug("Entering PageNameFilter.doFilter()");
String pageName = getPageTitle(request, response);
System.out.println("Page Name inside PageNameFilter " + pageName);
PageNameRenderRequestWrapper pageNameRequestWrapper = new PageNameRenderRequestWrapper(request);
pageNameRequestWrapper.setPageName(pageName);
filterChain.doFilter(pageNameRequestWrapper, response);
logger.debug("Exiting PageNameFilter.doFilter()");
}

private String getPageTitle(PortletRequest request, PortletResponse response){
try {
if (navigationSelectionModelHome != null) {
NavigationSelectionModelProvider provider =
navigationSelectionModelHome.getNavigationSelectionModelProvider();
NavigationSelectionModel model =
provider.getNavigationSelectionModel((ServletRequest)request, (ServletResponse)response);
NavigationNode navigationNode = (NavigationNode) model.getSelectedNode();
ContentNode contentNode = navigationNode.getContentNode();
if( contentNode.getObjectID().getUniqueName() != null){
logger.debug("The portlet is getting rendered on " + contentNode.getObjectID().getUniqueName());
}else{
logger.debug("The portlet is getting rendered on " + contentNode.getObjectID());
}
String pageTitle = contentNode.getTitle(request.getLocale());
if(pageTitle == null){
pageTitle = contentNode.getTitle(new Locale("en"));
if(pageTitle == null){
pageTitle = contentNode.getObjectID().getUniqueName();
if(pageTitle == null)
pageTitle = contentNode.getObjectID().toString();
}
}
return pageTitle;
}
} catch (ModelException e) {
logger.error("Error in PageNameFilter.getPageTitle() " + e.getMessage(),e);
}
return null;
}

private NavigationSelectionModelHome navigationSelectionModelHome;
public void init(FilterConfig filterConfig) throws PortletException {
try {
InitialContext context = new InitialContext();
navigationSelectionModelHome = (NavigationSelectionModelHome) context
.lookup(NavigationSelectionModelHome.JNDI_NAME);
} catch (NamingException e) {
logger.error("Error in PageNameFilter.init() " + e.getMessage(),e);
}
}
public void destroy() {
}
}


The PageNameFilter implements RenderFilter and in the doFilter() method it is passing control to getPageTitle method to get title of the page in the current locale or English. The getPageTitle, method is making use of the NavigationSelectionModel and callings its model.getSelectedNode() to first get the users current page and then reading the contentNode of the page from it.

I had to create PageNameRenderRequestWrapper that will override the actual RenderRequest and pass pageName as request attribute. This is how my PageNameRenderRequestWrapper looks like

package com.webspherenotes.portlet.filter;

import java.util.Enumeration;
import java.util.Vector;

import javax.portlet.RenderRequest;

public class PageNameRenderRequestWrapper extends javax.portlet.filter.RenderRequestWrapper{
private String pageName;
public static final String PAGENAME_ATTRIBUTE ="com.webspherenotes.filter.pageName";

public PageNameRenderRequestWrapper(RenderRequest request) {
super(request);
}

public Object getAttribute(String name) {
if(name.equals(PAGENAME_ATTRIBUTE)){
return pageName;
}
return super.getAttribute(name);
}

public Enumeration getAttributeNames() {
Enumeration originalAttributeEnum = super.getAttributeNames();
Vector wrappedEnum = new Vector();
while(originalAttributeEnum.hasMoreElements()){
wrappedEnum.add(originalAttributeEnum.nextElement());
}
wrappedEnum.add(PAGENAME_ATTRIBUTE);
return wrappedEnum.elements();
}

public String getPageName() {
return pageName;
}

public void setPageName(String pageName) {
this.pageName = pageName;
}



}


Inside the portlet you can read com.webspherenotes.filter.pageName request attribute to get name of the page where portlet is getting rendered