Un tutoriel sur la communication interportlet. Les ressources sur le sujet sont encore rares, il m’a semblé nécessaire de faire ce tutoriel assez rapidement du coup.
Si vous suivez les autres tutoriels de ce site sur la programmation JAVA, il arrive bien en avance de ce qui était prévu. Mais je le réintégrerai plus tard dans un tutoriel plus long consacré aux portlets.
Pour l’instant, ce tutoriel va traiter de la communication entre portlet conforme à la JSR286.
En particulier nous ne traiterons que de la communication par passage d’événement. Cette communication étant à nos yeux la plus compatible avec une vision composant. Pour ceux qui veulent en savoir plus, la ressource issue du blog xebia est la meilleure référence que j’ai trouvé. Dans cette ressource, vous retrouverez l’essentiel de ce qui est détaillé ici et une explication de l’autre solution de communication qu’est le partage d’état. Il y a aussi une discussion dans un des forums de JBoss.
Je précise que dans le cadre de ce tutoriel, les portlets ont été développés sous eclipse (mais ça n’a pas d’importance) et déployés sous JBoss Portal (version 2.7 + AS 4.2.3).
Cependant, le tutoriel s’adresse à tous développeurs de portlet 2.0, c’est à dire dans le cadre d’un développement de portlet pour un portail respectant la JSR286.
Partage d’état vs passage d’événement
Il y a deux façons de communiquer avec la JSR 286. La première solution est le partage d’état. Nous ne détaillerons pas dans ce tutoriel cette solution mais allons en expliquer le principe. La deuxième solution, celle que nous détaillerons ici en pratique est le passage d’événement. Ce paragraphe va donc exposer les principes de ces deux solutions et va nous permettre de justifier pourqoi le passage d’événement nous semble plus proche d’une approche composant et donc d’un développement orienté vers l’intégration de composants.
Le partage d’état : dans cette solution, chaque portlet déclare les paramètre qu’il partage avec les autres portlets et chaque portlet déclare les paramètres des autres portlets qu’il veut pouvoir atteindre. Ainsi, au moment ou un portlet effectue son rendu (calcule sa vue), il rend ses paramètres visibles ou accéde à ceux des autres.
Le passage d’événement : dans cette solution, chaque portlet déclare les événements qu’il peut lever et les événements qu’il peut écouter. Un portlet léve ensuite un événement, et les portlets écouteurs sont automatiquement au courant.
Discussion : La première solution (celle du passage d’état) peut être recommandée quand les paramètres échangés ne sont utiles que pour l’affichage. La solution est facile à mettre en place, il suffit de déclarer dans le descripteur du portlet les paramétres partagés. Cependant, cette solution n’est pas recommandé quand les paramétres doivent être traités (c’est à dire quand ils risquent d’être modifiés par un calcul effectué par un autre portlet) car les paramètres ne sont échangés qu’au moment de l’appel à la méthode Render. Mais la principale critique est que cette solution rend les portlets fortement dépendants les uns des autres puisque ils supposent l’existence de ces paramètres afin de rendre leur affichage. La deuxième solution quand à elle permet d’effectuer des traitements sur les paramètres et reste très proche d’une vision composant. Le mécanisme d’événements garantit que si le paramètre existe il y aura traitement, et que si le paramètre n’existe pas, il y aura un fonctionnement par défaut.
Passage d’événement pratique
Alors en pratique comment cela fonctionne? L’exemple que je vous propose est très simple: nous développons deux portlets, un PortletA et un PortletB. Chaque portlet a son propre projet dans Eclipse (bien que j’aurai pu faire un seul projet, mais pour l’exemple, je voulais détailler un cas ou les portlets auraient pu être développés sans connaissance l’un de l’autre). Le PortletA sera l’écouteur d’événement, le PortletB se chargera de l’envoi. L’événement levé permet d’échanger une donnée de type String.
Il est possible d’échanger autre chose que des String. Tout objet serializable peut être échangé par passage d’événement.
Les erreurs classiques à éviter (si je le dis c’est que je suis passé par là croyez-moi
) :
- Ne pas utiliser la bonne api de portlet. N’oubliez pas que nous faisons des portlets avec JSR 286 donc on utillise la nouvelle api.
- Si vous faites des JSP, n’oubliez toujours pas que vous faites des portlets version 2. Donc le taglib à utiliser est : <%@ taglib uri=”http://java.sun.com/portlet_2_0″ prefix=”portlet” %>
- N’oubliez toujours pas que vous faites des portlets 2.0. Donc dans vos descripteurs portlet.xml, les attributs du noeud portlet-app changent : <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”>
Il faut déclarer le type des paramétres échangés, ou des event levés si vous préférez. Pour cela, il faut modifier le descripteur de l’application portlet.
Il faut ajouter un noeud <event-definition> comme fils du noeud <portlet-app> dans votre fichier portlet.xml.
PortletB
Dans l’exemple, le descripteur portlet.xml associé au projet du portletB devient :
<?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>PortletB</portlet-name>
<portlet-class>tuto.portlet.PortletB</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Enverra des infos à Portlet A</title>
</portlet-info>
<supported-publishing-event>
<qname>totoString</qname>
</supported-publishing-event>
</portlet>
<event-definition>
<qname>totoString</qname>
<value-type>java.lang.String</value-type>
</event-definition>
</portlet-app>
J’ai décidé de passer des String uniquement comme paramétre et d’appeler cet événement “totoString”.
Au niveau du code, j’ai décidé de faire un simple portlet qui charge un jsp et qui léve l’événement totoString lors d’une action. L’action est réalisé lors d’un clic sur un lien dans le portlet (cf portletB.jsp).
Le code du PortletB :
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
public class PortletB extends GenericPortlet
{
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws IOException, PortletException
{
rResponse.setContentType(”text/html”);
PortletRequestDispatcher prd;
prd = getPortletContext().getRequestDispatcher(rResponse.encodeURL(”/WEB-INF/jsp/portletB.jsp”));
prd.include(rRequest, rResponse);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException,IOException {
System.out.println(”processAction”);
this.sendEvent(response);
}
public void sendEvent(ActionResponse response)
{
String _toto = “passée en event”;
response.setEvent(”totoString”, _toto);
}
}
Le fichier portletB.jsp :
<%@ page language=”java” contentType=”text/html; charset=UTF-8″
pageEncoding=”UTF-8″%>
<%@ taglib uri=”http://java.sun.com/portlet_2_0″ prefix=”portlet” %>
<portlet:defineObjects/>
HelloWorld !!! Portlet B!
<a href=”<portlet:actionURL/>” >Envoi de l’event</a>
PortletA
Le PortletA est le portlet traitant un événement. Il doit donc implémenter l’interface EventPortlet et sa méthode processEvent(EventRequest request, EventResponse response). La méthode processEvent sera automatiquement appelé lorsque un événement du type de ceux déclaré dans le descripteur portlet.xml sera levé par un autre portlet.
Le descripteur portlet.xml de PortletA :
<?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>PortletA</portlet-name>
<portlet-class>tuto.portlet.PortletA</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Recevra des infos de Portlet B</title>
</portlet-info>
<supported-processing-event>
<qname>totoString</qname>
</supported-processing-event>
</portlet>
<event-definition>
<qname>totoString</qname>
<value-type>java.lang.String</value-type>
</event-definition>
</portlet-app>
Le code de PortletA :
import java.io.IOException;
import java.io.PrintWriter;
import javax.portlet.Event;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.EventPortlet;
public class PortletA extends GenericPortlet implements EventPortlet
{
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws IOException, PortletException
{
if(rRequest.getParameter(”stringEvent”)!=null)//Action avec Event
{
rResponse.setContentType(”text/html”);
PrintWriter writer = rResponse.getWriter();
writer.write(”Event value of totoString : ” + rRequest.getParameter(”stringEvent”));
writer.close();
}
else //Action sans Event
{
rResponse.setContentType(”text/html”);
PortletRequestDispatcher prd;
prd = getPortletContext().getRequestDispatcher(rResponse.encodeURL(”/WEB-INF/jsp/portletA.jsp”));
prd.include(rRequest, rResponse);
}
}
public void processEvent(EventRequest request, EventResponse response)
{
Event event = request.getEvent();
String _toto = “vide”;
if(event.getName().equals(”totoString”))
{
_toto = (String)event.getValue();
response.setRenderParameter(”stringEvent”, _toto);
}
}
}
Le jsp de PortletA n’a aucune importance étant donné que c’est uniquement du HTML au final. Pour varier les plaisirs j’ai préféré utiliser un printWriter pour l’affichage après traitement de l’événement. Le but étant juste ici de constater visuellement que le PortletA a bien reçu l’événement. Dans un cas concret, il n’est pas forcément nécessaire de modifier le rendu du PortletA une fois l’événement traité.
Rendu dans JBoss Portal
Une fois déployés les deux portlets s’affichent ainsi :
Une fois cliqué sur le lien “Envoi de l’event” :
J’espére que ce tutoriel court et rapide vous aura suffisament éclairé et vous fera gagner du temps. Le plus difficile concernant l’IPC étant encore de trouver de la documentation.
Vous pouvez télécharger les sources de ce tutoriel.







