When the Java Portlet Specification(JSR 286) early draft was released last year, i had written a blog on the Eventing. Recently the proposed final draft was released, it has some changes in the eventing feature. In this blog i will explain the feature with an example.

To create portlets that use the eventing feature, follow these steps(snippets from EventingMap sample application):

1.Declare the events in the portlet.xml

    1.1 Set the event definition at the portlet application level. This specifies the event name and the object type.
        Note: The object must be serializable and must be instrumented with valid JAXB annotation

   <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
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="myPortletApp" version="2.0">
<portlet>
. . .
. . .
</portlet>

<event-definition>
<qname xmlns:x="http:sun.com/mapevents">x:Continent</qname>
<value-type>com.sun.portal.portlet.mapevent.Continent</value-type>
</event-definition>

</portlet-app>


@XmlRootElement
public class Continent implements Serializable {
public Continent() {
}
private String name;
private String description;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description)
this.description = description;
}
}

  1.2 In the portlet section, specify the event name defined above for those portlets that want to publish this event.

        <portlet>
<description>ContinentPortlet</description>
<portlet-name>ContinentPortlet</portlet-name>
            .................
<supported-publishing-event>
<qname xmlns:x="http:sun.com/events">x:Continent</qname>
</supported-publishing-event>

        </portlet>


    1.3 In the portlet section, specify the event name defined above for those portlets that want to process this event.

    <portlet> 
<description>ContinentMapPortlet</description>
<portlet-name>ContinentMapPortlet</portlet-name>
. . .
<supported-processing-event>
<qname xmlns:x="http:sun.com/events">x:Continent</qname>
</supported-processing-event>

</portlet>
  <portlet>
<description>ContinentInfoPortlet</description>
<portlet-name>ContinentInfoPortlet</portlet-name>
. . .
<supported-processing-event>
<qname xmlns:x="http:sun.com/events">x:Continent</qname>
</supported-processing-event>

</portlet>
. . .
</portlet-app>

    
2. Issue an event in the portlet that was specified as supported-publishing-event in the portlet.

public class ContinentPortlet extends GenericPortlet {

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException,IOException {

QName qname = new QName("http:sun.com/mapevents" , "Continent")
String value = request.getParameter("continent");
Continent continent = new Continent();
continent.setName(value);

ResourceBundle rb = getPortletConfig().getResourceBundle(request.getLocale());
continent.setDescription(rb.getString(value));
response.setEvent(qname, continent);
}
. . .
}

3. Process the event in the portlet that has specified as supported-processing-event in the portlet

public class ContinentInfoPortlet extends GenericPortlet {

public void processEvent(EventRequest request, EventResponse response) {

Event event = request.getEvent();
if(event.getName().equals("Continent")){
Continent payload = (Continent)event.getValue();
response.setRenderParameter("continentDescription",
payload.getDescription());
}

}

. . .

}


public class ContinentMapPortlet extends GenericPortlet {
public void processEvent(EventRequest request, EventResponse response) {

Event event = request.getEvent();
if(event.getName().equals("Continent")){
Continent payload = (Continent)event.getValue();
response.setRenderParameter("continentName", payload.getName());
}

}
. . .
}

You can download the binary file EventingMap.war to deploy and run the sample application in OpenPortal Portlet Container 2.0 Beta2(or latest). You can also get the source for the sample.

The figure shows the World Map, Continent Information, and Continent Map Portlets that participate in the event. Clicking on any continent in the World Map triggers an event. This event is processed by the Continent Information and Continent Map Portlets to show the relevant information.


Comments:

Thank you for the example and explanation of eventing.
Can you please explain how the text that appears in the Continent Information paragraph is obtained? The WAR file correctly deployed and the application works. I also downloaded all the source code. But I don't see where the information comes from that shows continent information (getResourceBundle() in ContinentPortlet.java??).

Posted by GeoInt on April 21, 2008 at 08:46 PM IST #

Good to know that it was useful.
See the following snippet of code in ContinentPortlet.java
-------------------------------------------------------------
String value = request.getParameter("continent");
....
ResourceBundle rb = getPortletConfig().getResourceBundle(request.getLocale());
continent.setDescription(rb.getString(value));
-------------------------------------------------------------
The call getResourceBundle(..) returns the resource bundle specified in the portlet.xml for the portlet, ContinentPortlet. As you can see from portlet.xml(<resource-bundle>ContinentPortlet</resource-bundle>), this is ContinentPortlet.properties. The "value"(i.e value of the request parameter "continent") is a key in ContinentPortlet.properties. So rb.getString(value) will return the description of the continent. I hope i am clear. Let me know if you need more info.

Posted by Deepak Gothe on April 22, 2008 at 12:03 PM IST #

Yes, thank you, now I understand how the ResourceBundle works. I am trying to modify your example to use an openlayers.org dynamic map. There is an OpenLayers javascript function that will capture the coordinates where the user clicks on the map. I need to pass these coordinates as parameters in the portlet:actionURL tag. All examples I can find show parameter values as strings. Is there a way to pass a value that is not a string, ie, a double or float? I have tried using string concatenation to build the string like I would for a regular URL that had parameters that were not strings. But I can't seem to get the syntax correct within the portlet:actionURL tag.

Posted by GeoInt on April 22, 2008 at 06:08 PM IST #

It would help the community if you pose the questions on the alias(https://portlet-container.dev.java.net/servlets/ProjectMailingListList)

Yes parameters are always Strings..

try..

<portlet:actionURL>
<portlet:param name="int1" value="10" />
<portlet:param name="float1" value="10.02" />
</portlet:actionURL>

Posted by Deepak Gothe on April 23, 2008 at 02:08 PM IST #

Thank you. I am still unable to get my application to work. I will pose my questions elsewhere, as you suggest. However, I cannot access the link you gave for the alias. Would this be the same as posting to the forum: http://forum.java.sun.com/category.jspa?categoryID=111 You had suggested posting to that forum last week when I posted a question about EventingMap to the wrong forum.

Posted by GeoInt on April 24, 2008 at 02:03 AM IST #

There was an extra ), so the URL was not accessible. Try https://portlet-container.dev.java.net/servlets/ProjectMailingListList

If you pose questions to the alias, you might responses from community you might have faced similar issues. Sure you can also try posting to the forum. While posting the question to the alias/forum, can you provide code snippets so that we know what problem you are facing. Thanks.

Posted by Deepak Gothe on April 24, 2008 at 12:38 PM IST #

Hi Deepak,
I am getting an exception while setting the event in response object.
The code is :
response.setEvent(qName, EventsData);
Where EventsData class stores a Map of <String, MyObjects>.MyObjects class has properties of objects type (arrays) say obj1 [] ,obj2 etc.
when I call response.setEvent(qName, EventsData), the application throws
exception:
Cannot serialize to xml for the value type:com.portlet.Events of event: {http://sunevents.com}myEvents
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 117 counts of IllegalAnnotationExceptions
Class has two properties of the same name "obj1"
and same for the other properties.
All of the objects implements Serializable.
Please help me out

Posted by Hariom Tiwari on March 06, 2009 at 12:28 PM IST #

Hi Hariom,
We use JAXB serialization for eventing. Check this thread for possible clues to your issue.

http://forums-beta.sun.com/thread.jspa?messageID=9798273

Posted by Deepak Gothe on March 06, 2009 at 02:34 PM IST #

Hi Deepak,
If we want to make this test of eventing in the liferay Is there is a need to add portal.container.impl=sun in the Portal-ext.properties

is it compulsary to add

Posted by srinivas on July 21, 2009 at 09:17 PM IST #

Hi Srinivas,
Not required to add portal.container.impl=sun in the portal-ext.properties. As the eventing sample mentioned in this blog is basic. It should work with portlet-container.impl=internal(which is the default).

Posted by Deepak on July 23, 2009 at 10:29 AM IST #

Hi,

The link: https://portlet-container.dev.java.net/files/documents/5463/80971/EventingMap.war , doesnt working.
Can anybody send me a sample on a email: boobu@go2.pl, or give my some other link..

Thanks!

Posted by Beata on October 21, 2009 at 05:38 PM IST #

Did you check the link for all the samples in https://portlet-container.dev.java.net/public/Samples.html

Posted by Deepak on October 21, 2009 at 05:50 PM IST #

I have problems with connecting to https://portlet-container.dev.java.net/

Posted by Beata on October 21, 2009 at 06:40 PM IST #

I am trying to make a sample with two war files, communicating via events.
One portlet is a producer and the second is a customer. Both have defined the same data model, eg. Order.class - so the producer has a Order class, and the customer has a Order class.
I am trying to send event from producer to customer, with Order object attached. Customer recives the event, gets the object of the event. The Order object is a POJO class marked with JAXB annotation. The object is properly send and recived:

javax.portlet.Event event = eventRequest.getEvent();
Order order = (Order) event.getValue(); << but during the class cast I get the ClassCastException, because the Order.class is loaded with different class loader, and I cannot cast it.

I do not know if this exception is caused because the serialization mechanism is not working, and I have something misconfigure, or maybe deploying the same classes in 2 portlets is not good solution.

I could externalize common classes to separate jar and deploy it on the server common class loader (on the server class path), but maybe there is some other way?

I would appreciate any clues.

Posted by 192.100.112.211 on October 21, 2009 at 06:57 PM IST #

I am trying to make a sample with two war files, communicating via events.
One portlet is a producer and the second is a customer. Both have defined the same data model, eg. Order.class - so the producer has a Order class, and the customer has a Order class.
I am trying to send event from producer to customer, with Order object attached. Customer recives the event, gets the object of the event. The Order object is a POJO class marked with JAXB annotation. The object is properly send and recived:

javax.portlet.Event event = eventRequest.getEvent();
Order order = (Order) event.getValue(); << but during the class cast I get the ClassCastException, because the Order.class is loaded with different class loader, and I cannot cast it.

I do not know if this exception is caused because the serialization mechanism is not working, and I have something misconfigure, or maybe deploying the same classes in 2 portlets is not good solution.

I could externalize common classes to separate jar and deploy it on the server common class loader (on the server class path), but maybe there is some other way?

I would appreciate any clues.

Posted by Beata on October 21, 2009 at 06:59 PM IST #

I forgot to write, that I am using: Liferay & Tomcat 6.0 bundle, and Java JDK 6.0.

Posted by Beata on October 21, 2009 at 07:27 PM IST #

If you are using Liferay & Tomcat 6.0 bundle, and Java JDK 6.0, then the option of having a separate jar for common classes in server classpath is the only solution. But if you don't want to do it, you can change the portlet container implementation in liferay to use OpenPortal Portlet Container 2.x. This can be done by setting setting the property "portlet.container.impl=sun" in portal-ext.properties. You need for Liferay 5.2.3 for this to work. After doing the change you need to redploy the portlet wars. As Portlet Container 2.x uses JAXB serialization for eventing, you don't need a separate jar in server classpath.

Posted by Deepak on October 22, 2009 at 02:29 PM IST #

Thank you very much for the reply. I did set the property: portlet.container.impl=sun" in portal-ext.properties file. It switch on the JAXB serialization. But then, I have a problem with JSF:

java.lang.IllegalStateException: Application was not properly initialized at startup, could not find Factory: javax.faces.lifecycle.LifecycleFactory
at javax.faces.FactoryFinder$FactoryManager.getFactory(FactoryFinder.java:725)

My web.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="MyWPSRFPortletConsumerProj" version="2.5" metadata-complete="true"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>MyWPSRFPortletConsumer</display-name>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.jsp</param-value>
</context-param>
<context-param>
<param-name>com.ibm.ws.jsf.JSP_UPDATE_CHECK</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>com.ibm.ws.jsf.LOAD_FACES_CONFIG_AT_STARTUP</param-name>
<param-value>true</param-value>
</context-param>

<context-param>
<param-name>javax.portlet.faces.renderPolicy</param-name>
<param-value>ALWAYS_DELEGATE</param-value>
</context-param>
<context-param>
<param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
<param-value>org.jboss.portletbridge.application.FaceletPortletViewHandler</param-value>
</context-param>
<context-param>
<param-name>org.ajax4jsf.RESOURCE_URI_PREFIX</param-name>
<param-value>rfRes</param-value>
</context-param>
<context-param>
<param-name>org.ajax4jsf.SKIN</param-name>
<param-value>renewal</param-value>
</context-param>
<context-param>
<param-name>org.richfaces.LoadStyleStrategy</param-name>
<param-value>DEFAULT</param-value>
</context-param>
<context-param>
<param-name>org.richfaces.LoadScriptStrategy</param-name>
<param-value>DEFAULT</param-value>
</context-param>

<context-param>
<param-name>com.ibm.faces.DISABLE_JWL_MULTIPART_CONTEXT</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.RESOURCE_EXPIRE_MS</param-name>
<param-value>31536000000</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.DATETIME_ASSIST_STRICTNESS</param-name>
<param-value>1</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.NUMBER_ASSIST_STRICTNESS</param-name>
<param-value>1</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.USE_UNENCODED_CONTEXT_PATH</param-name>
<param-value></param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.JS_RESOURCE_SERVLET_URL_PATTERN</param-name>
<param-value>/.ibmjsfres/*</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.JS_RESOURCE_SERVLET_CACHE</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.ENCODING_MAPPING</param-name>
<param-value>converter.properties</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.USE_HXCLIENT_FULL</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.ENCODE_DATA</param-name>
<param-value>true</param-value>
</context-param>

<context-param>
<param-name>com.ibm.faces.TURN_OFF_AJAX_PORTAL_60_PATH</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.ibm.faces.MAX_REQUEST_CONTENT_SIZE</param-name>
<param-value>0</param-value>
</context-param>
<context-param>
<description>
Number of Views to be stored in the session when Enhanced State Saving is being used.
Default is 15.</description>
<param-name>com.ibm.faces.ENHANCED_SERVER_STATE_SAVING_SESSION_STORED_VIEWS</param-name>
<param-value>15</param-value>
</context-param>

<filter>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>richfaces</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>richfaces</filter-name>
<url-pattern>/faces/rfRes/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<!--listener>
<listener-class>pagecode.MyListener</listener-class>
</listener-->

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>JavaScript Resource Servlet</servlet-name>
<servlet-class>com.ibm.faces.webapp.JSResourceServlet</servlet-class>
<load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>
/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JavaScript Resource Servlet</servlet-name>
<url-pattern>/.ibmjsfres/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>

I am using RhichFaces 3.3.1., and PortletBride :my faces-config.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
<application>
<view-handler> org.jboss.portletbridge.application.PortletViewHandler</view-handler>
<state-manager>org.jboss.portletbridge.application.PortletStateManager</state-manager>
</application>
<factory>
<faces-context-factory>org.jboss.portletbridge.context.FacesContextFactoryImpl</faces-context-factory>
</factory>

<managed-bean>
<managed-bean-name>producerBean</managed-bean-name>
<managed-bean-class>com.nsn.ProducerManagedBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

</faces-config>

So you say that the only way is to externalize the jar? Because on the other hand I would have problems with JSF.
Maybe I have something configure wrong.

Best Regards
Beata

Posted by Beata on October 26, 2009 at 02:46 PM IST #

Its not clear how enabling JAXB would affect JSF initialization.

Posted by Deepak on October 26, 2009 at 03:27 PM IST #

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2009 by deepakg