Thursday, February 25, 2010

Coordination between portlets in JSR-286

To provide coordination JSR-286 provides 2 mechanism
1.Event: Portlet event that portlet can recieve and send.
2.Public Render Parameter : Render states that can be shared between portlets.

Event :

In JSR-168 :
The only way to achive eventing was through portlet session.
Limitation : Portlet has to be in the same web application.

In JSR-286 :
JSR 286 (Portlet 2.0) defines a lifecycle for events, so that eventing is possible between portlets that are in different web applications.

Definition Of Event in JSR -286 :
An event is lifecycle operation occuring before rendering phase. It is a loosely coupled , acting as a agent for communication between portlets.
Events allow portlets to respond on actions or state changes not directly related to an interaction of the user with the portlet.

A portlet can declare events in its deployment descriptor by using the event-definition element in the portlet application section.
In the portlet section, each portlet specifies the events it would like to publish through the supported-publishing-event element and
the events it would like to process through the supported-processing-event element.

The supported-publishing-event and supported-processing-event elements must reference the event name defined in the portlet application section in an event-definition element.

The portlet creates events using the setEvent() method during action processing. The events are processed by the portlet container after the action processing has finished.
Portlets can also create events during the event phase by calling the setEvent() method on EventResponse.

To receive events, the portlet must implement the javax.Portlet.EventPortlet interface. The portlet container calls the processEvent() method
for each event targeted to the portlet with an EventRequest and EventResponse object. The portlet can access the event that triggered the current
process event call by using the EventRequest.getEvent() method. This method returns an object of type Event encapsulating the current event name and value.

Event names are represented as QNames to identify them uniquely. The event name can be retrieved by using the getQName() method that returns the complete
QName of the event, or by using the getName() method that returns only the local part of the event name. The value of the event must be based on the type
defined in the deployment descriptor.

So this all about theory time for some practical.

For this simple example I am going to use plugin sdk liferay version 5.2.3

Step 1:
create 2 portlets in plugins environment.Creating portlet in plugin is very simple just execute below command
Go to {your_work_space}\plugins\portlets> and execute command
create hello "Hello World" this will create portlet with portlet display name = "Hello World" and portlet name = hello

I am creating here 2 portlets with name test1 and test2

Step 2: Go to your 1st portlet below location in our present case it is test1 i.e

{your_work_space}\plugins\portlets\test1-portlet\docroot\WEB-INF

open porlet.xml file. In that add first Event definition as below

e.g.
<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>
<!-- Event Definition-starts -->
<event-definition>
<qname xmlns:x="http:kamal.com/events">x:Name
<value-type>java.lang.String
<!-- Event Definition-ends -->
</event-definition>
</portlet-app>

Step 3: Now we need to publish event
We will make test1-portlet as Event Publishing portlet as well. So open again portlet.xml file same as step 2
Add supported-publishing-event tag like below.

<supported-publishing-event>
x:Name
</supported-publishing-event>

Now your portlet.xml file structure will be like this.

<?xml version="1.0"?>

<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>

<..>
<!-- supported-publishing-event- starts -->
<supported-publishing-event>
x:Name
</supported-publishing-event>
<!-- supported-publishing-event- ends -->
</portlet>
<event-definition>
<qname xmlns:x="http:kamal.com/events">x:Name
<value-type>java.lang.String
</event-definition>
</portlet-app>


Step 4: Now our Event publishing portlet is ready with required configuration , now we need to do same configuration for
Event Processing portlet. Now open portlet.xml for test2-portlet and modify it as above with minor change in place of
<supported-publishing-event> use <supported-processing-event> thats it. Now structure will look like this.

<?xml version="1.0"?>

<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>
<!-- here other portlet related content will come like portlet-name , diaply-name etc-->
<..>
<supported-processing-event>
x:Name
</supported-processing-event>
</portlet>
<event-definition>
<qname xmlns:x="http:kamal.com/events">x:Name
<value-type>java.lang.String
</event-definition>
</portlet-app>


Step 5: Now all configuration part is over , now we will modify java files
Go to test1-portlet's JSPPortlet.java at location {your_work_space}\plugins\portlets\test1-portlet\docroot\WEB-INF\src\com\sample\jsp\portlet
In processAction method add below code
QName qname = new QName("http:kamal.com/events" , "Name");
String value = "Hurray we have done it";
actionResponse.setEvent(qname, value);

and add needful imports
import javax.xml.namespace.QName;

Step 6: Now Go to test2-portlet's JSPPortlet.java at location {your_work_space}\plugins\portlets\test2-portlet\docroot\WEB-INF\src\com\sample\jsp\portlet
Add new method to process event

public void processEvent(EventRequest request, EventResponse response) {
_log.info("I m in process event");
Event event = request.getEvent();
if(event.getName().equals("Name")) {
String displayText = (String)event.getValue();
response.setRenderParameter(
"displayText", displayText);
}
}

and do needful imports
import javax.portlet.Event;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;

Step 7: Now we are done java part as well need to do minor coding in jsp to see our hard work live.
Open view.jsp of test1-portlet to provide a link to invoke event
e.g.
<%
PortletURL actionURL = renderResponse.createActionURL();
%>
<a href="<%= actionURL.toString() %>">click here to invoke event</a>
do needful imports if required
<%@ page import="javax.portlet.PortletURL" %>

Step 8: Open view.jsp of test2-portlet to show output
just add following line
<%= renderRequest.getParameter("displayText") %>
I have not done any thing extra , I tried to focus on concept and its implementation.

Start the server and if everything is working fine add both portlet test1 and test2 on a page and click the link
provided on test1 portlet you will see the output on portlet test2.

Pls do post your feedbacks.