Table of contents
Introduction
Once a portlet is placed on a portal page, its data is not directly shared with other portlets. IPC (Inter Portlet Communication) defines the ways that a Portlet can interact and communicate with another Portlet. There are four ways to make inter portlet communication :
- Using Portlet Session
- Using Public Render Parameters
- Using Events
- Using AJAX
In this post, we will see how to communicate between none-JSF portlets using events.
Events
JSR 286 (Portlet 2.0) introduced portlet communication using events. A new life cycle was introduced that occurs before the rendering phase aiming to handle event :
Below steps to follow to make a Portlet communication using events:
- Define the event in the Portlet.xml file
The first step is to define the event that will be fired and processed by portlets. The definition is set in the portlet configuration file Portlet.xml. Below the declaration :
1234<event-definition xmlns:event="http://www.roufid.com"><qname>identifier</qname><value-type>java.lang.String</value-type></event-definition>
Qname : Give a unique identifier in the namespace
Value-type : Type of object stored in the event
- Sender portlet
- Declare that the portlet is publishing the defined event in portlet.xml
To send the event, the portlet must specify that it’s publishing it. Add in portlet.xml file the <supported-publishing-event> property that refers to the event identifier. Example:
123<supported-publishing-event><qname xmlns:x="http://www.roufid.com">event:identifier</qname></supported-publishing-event> - Firing the event
Firing event from the sender portlet is done by using QName class that refers to the defined event by giving its namespace and identifier.
123456public void portletActionURL(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException {// Firing the event.QName qName = new QName("http://www.roufid.com ", "identifier");actionResponse.setEvent(qName, dataYouWantToSend);}
- Declare that the portlet is publishing the defined event in portlet.xml
- Receiver portlet
- Declare that the portlet is processing the defined event in portlet.xml
The receiver portlet must declare that it’s processing the defined event by adding the property <supported-processing-event> in the portlet.xml123<supported-processing-event><qname xmlns:x="http://www.roufid.com">event:identifier</qname></supported-processing-event> -
Process the event
Fired events are processed in the processEvent phase. This phase was added as a new lifecyle in Portlet 2.0 specification.1234567891011121314151617public class YourPortlet extends MVCPortlet {@ProcessEvent(qname = "{http://www.roufid.com}identifier")public void carSelectionEvent(EventRequest eventRequest, EventResponse eventReponse)throws PortletException, IOException {// Getting the fired event from the event request.Event event = eventRequest.getEvent();// Getting the value of the sent parameter.String value = (String) event.getValue();// Setting the result in the render parameter.eventReponse.setRenderParameter("parameterId", value);}}The value of the event must be based on the type defined in the deployment descriptor (Portlet.xml).
- Declare that the portlet is processing the defined event in portlet.xml
Concrete example
Let’s see a full example of using the IPC with event.
The code source is available on Github. Download the source
Let’s consider two portlets :
- CarList (Portlet 1): This portlet shows a list of cars. The user can select a car from the list. When the user select a car, an event will be fired with the selected car identifier. The car information will be displayed in the second portlet.
- CarInformation (Portlet 2): If an event is fired, it will be processed in the second portlet which will display the car information.
Defining the event
First step is to define the event that will be fired and processed by portlets in the portlet.xml file. If the portlets that handle the event are in the same Liferay Plugin Project, so only one definition of the event must be in the portlet.xml file. If not, each portlet must declare it in its portlet.xml file.
In this example, the two portlets are in the same Liferay Plugin Project. So they are defined in the same portlet.xml file along with the event.
The event that will be fired and processed will be called “SelectCarEvent” and will store the car identifier which is a java.lang.String. The declaration is as following :
1 2 3 4 5 |
<!-- Defining the event --> <event-definition xmlns:event="http://www.roufid.com"> <qname>event:selectCarEvent</qname> <value-type>java.lang.String</value-type> </event-definition> |
Portlet 1 : CarList
- Portlet.xml
Defining the CarList portlet in the portlet.xml file. This portlet will publish the “SelectCarEvent” event. It must define the <supported-publishing-event> property :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<portlet> <portlet-name>car-list</portlet-name> <display-name>Car List</display-name> <portlet-class>com.roufid.tutorials.portlet.CarList</portlet-class> <init-param> <name>view-template</name> <value>/html/carlist/view.jsp</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Car List</title> <short-title>Car List</short-title> <keywords></keywords> </portlet-info> <security-role-ref> <role-name>administrator</role-name> </security-role-ref> <security-role-ref> <role-name>guest</role-name> </security-role-ref> <security-role-ref> <role-name>power-user</role-name> </security-role-ref> <security-role-ref> <role-name>user</role-name> </security-role-ref> <!-- The carList portlet will publish the event --> <supported-publishing-event xmlns:event="http://www.roufid.com"> <qname>event:selectCarEvent</qname> </supported-publishing-event> </portlet> |
- Selecting the car
In the jsp view, i used an actionURL to process a portlet action from which the event will be fired. Below the code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme"%> <%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%> <%@taglib uri="http://liferay.com/tld/aui" prefix="aui"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <portlet:defineObjects /> <liferay-theme:defineObjects /> <!-- Defining the action URL --> <portlet:actionURL var="selectCar" windowState="normal" name="selectCarAction" /> <table class="table table-striped"> <thead> <tr> <th></th> <th>Id</th> <th>Year</th> <th>Brand</th> <th>Color</th> <th>Action</th> </tr> </thead> <tbody> <c:forEach var="car" items="${cars}"> <!-- Defining a form for each car. --> <aui:form action="<%=selectCar%>" method="post" name="carList"> <input name="carId" value="${car.id}" style="visibility: hidden;" /> <tr> <td><img src="${car.imagePath}" width="50" height="50" /></td> <td><c:out value="${car.id}" /></td> <td><c:out value="${car.year}" /></td> <td><c:out value="${car.brand}" /></td> <td><c:out value="${car.color}" /></td> <td><input type="submit" name="selectButton" value="Select"> </td> </tr> </aui:form> </c:forEach> </tbody> </table> |
In the selectCarAction, the defined event (selectCarEvent) is fired with the selected car identifier:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class CarList extends MVCPortlet { public void selectCarAction(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException { // Getting the carId from the submitted form. String carId = ParamUtil.getString(actionRequest,"carId"); // Firing the event : selectCarEvent with the carId. QName qname = new QName("http://www.roufid.com", "selectCarEvent", "event"); actionResponse.setEvent(qname, carId); } } |
Portlet 2 : CarInformation
- Portlet.xml
Defining the CarInformation portlet in the portlet.xml file. This portlet will process the “SelectCarEvent” event. It must define the <supported-processing-event> property :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<portlet> <portlet-name>car-information</portlet-name> <display-name>Car Information</display-name> <portlet-class>com.roufid.tutorials.portlet.CarInformation</portlet-class> <init-param> <name>view-template</name> <value>/html/carinformation/view.jsp</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Car Information</title> <short-title>Car Information</short-title> <keywords></keywords> </portlet-info> <security-role-ref> <role-name>administrator</role-name> </security-role-ref> <security-role-ref> <role-name>guest</role-name> </security-role-ref> <security-role-ref> <role-name>power-user</role-name> </security-role-ref> <security-role-ref> <role-name>user</role-name> </security-role-ref> <!-- The carInformation portlet will process the event --> <supported-processing-event xmlns:event="http://www.roufid.com"> <qname>event:selectCarEvent</qname> </supported-processing-event> </portlet> |
- Processing the event
To process the event, the portlet must have a method annotated with javax.portlet.ProcessEvent with a qname parameter that refers to the event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class CarInformation extends MVCPortlet { @ProcessEvent(qname = "{http://www.roufid.com}selectCarEvent") public void carSelectionEvent(EventRequest eventRequest, EventResponse eventReponse) throws PortletException, IOException { // Getting the fired event from the event request. Event event = eventRequest.getEvent(); // Getting the value of the sent parameter. String carId = (String) event.getValue(); // Setting the result in the render parameter. eventReponse.setRenderParameter("carId", carId); } } |
Rendering after deployment :
The CarList portlet is on the left. CarInformation portlet is on the right.
Below the result after selecting BMW car.
The code source is available on Github. Download the source