Communication entre portlets JSR286 passage d'&#233;v&#233;nements

Antoine Seilles

Cet article explique comment mettre en place une communication entre portlets basée sur le système d'évènements de la JSR 286. Les portlets de test sont développés dans eclipse avec le plugin WTP et déployés dans le portail JBoss portal. Je me suis appuyé sur le blog de xebia pour faire ce tutoriel.

Commentez

- Titre : Communication entre portlets JSR286 passage d'&#233;v&#233;nements

- Auteur : Antoine Seilles

- Parution : 21 février 2010

- Mise à jour : 21 février 2010

- Licence : Copyright ® 2010 seilles antoine. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Partage d'état versus système d'évènements

- II. Systéme d'évènement, en pratique

- III. Une fois déployé dans JBoss Portal

- IV. Passer un objet en évènement

- V. A lire

- VI. Remerciements

I - Partage d'état versus système d'évènements

Il y a deux façons de communiquer avec la JSR 286 : le partage d'état et les évènements. Nous ne détaillerons que la deuxième solution dans ce document. Nous exposons dans cette première section les principes de ces deux solutions. Nous justifierons pourquoi les évènements nous semblent plus intéressants.

Le partage d'état : chaque portlet déclare les paramètres qu'il partage avec les autres portlets (ceux qu'il met à disposition et ceux auxquels il veut accéder). Au moment où un portlet effectue son rendu, il rend ses paramètres visibles et peut accéder à ceux des autres.
Les évènements : chaque portlet déclare les évènements qu'il lève et les évènements qu'il écoute. Quand un évènement est levé, les portlets écouteurs traitent l'évènement levé.
Discussion : dans un cas où les paramètres échangés ne servent qu'à l'affichage, la première solution est suffisante. Cette solution est facile à mettre en place mais ne convient que pour des portlets fortement liés. Avec la seconde solution par contre, le système est proche de celui des events et des listeners . Du coup, chaque portlet écouteur a nécessairement un comportement par défaut au cas où l'évènement qu'il écoute n'est pas lié. Les portlets sont donc beaucoup moins dépendants.

La seconde solution me paraît beaucoup plus intéressante et baucoup plus proche d'une vision composant. Nous allons maintenant montrer par l'exemple comment mettre cette solution en place.

II - Systéme d'évènement, en pratique

En pratique comment ça marche ?
L'exemple que je vous propose est très basique mais suffisant pour exposer le fonctionnement des évènements. Nous allons développer deux portlets : PortletA et PortletB.
Nous choisissons de faire deux projets distincts et deux war distincts pour bien montrer que dans le cas de la communication par évènements, les portlets sont indépendants.
Le PortletA sera l'écouteur, le PortletB lèvera l'évènement.
Un évènement est identifié par un nom. Le nom est de type String. L'évènement peut prendre une valeur, celle-ci étant aussi de type String. Au final, on peut passer tout objet Serializable par évènement d'un portlet à l'autre. Dans l'exemple, nous nous contenterons d'une simple String.
Le fonctionnement du système d'évènements en portlet rappelle le système des events et des listeners classiques en java. Mais il ressemble aussi au système de passage de paramètres des requêtes HTTP.
Erreurs à éviter :

- ne pas utiliser la bonne librairie pour les portlets. La communication inter-portlet, c'est la JSR 286, c'est la lib portlet 2.0 ;

- ne pas utiliser le bon taglib dans ses JSP. Encore une fois, on a besoin de portlet2.0.

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

- ne pas déclarer dans les descripteurs que l'on fait des portlets 2.0. Le noeud portlet-app du fichier portlet.xml devrait être :

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

Un lien pour récupérer la lib portlet2.0.

Chaque Portlet va devoir déclarer les paramètres qu'il léve et ceux qu'il écoute.
Cette déclaration se fait par le biais d'un noeud <event-definition> pour déclarer l'évènement (son nom et son type) et par le biais de noeuds <supported-publishing-event> pour déclarer l'évenement levé et <supported-processing-event> pour déclarer l'évènement écouté.
Dans notre exemple, l'évènement s'appelle totoString.
Les descripteurs (portlet.xml) des portlets A et B deviennent :
portletA 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>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>

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

Au niveau du code des portlets maintenant. La PortletA va simplement afficher HelloWorld par défaut et afficher la valeur de l'évènement totoString si celui-ci est levé.
Ce qui donne pour le code du PortletA :

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")) {
// traitement de l'event
_toto = (String) event.getValue();
response.setRenderParameter("stringEvent", _toto);
}
}
}

Vous observerez l'utilisation d'une méthode appelée processEvent. Celle-ci est appelée automatiquement si un évènement est levé. Il faut par contre tester dans cette méthode quel est le nom de l'event.
Dans mon cas, j'ai besoin de passer la valeur de l'event pour le rendu. Donc je crée un paramètre que je nomme ici stringEvent. Et dans ma méthode doView j'utilise effectivement la valeur de ce paramètre pour l'affichage.
Je ne copie pas le contenu de portletA.jsp qui ne fait qu'afficher HelloWorld. Vous le trouverez dans les sources liées à ce tutoriel.
Passons au PortletB maintenant. Le PortletB va lever l'évènement totoString quand l'utilisateur cliquera sur un lien.
J'utilise la méthode processAction pour traiter le clic sur le lien et cette méthode appelle la méthode sendEvent.

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);
System.out.println("envoi Event toto");
}
}

Le contenu de portletB.jsp ne fait qu'afficher le lien cliquable déclenchant l'action du portlet. Vous retrouverez ce fichier dans les sources liées à ce tutoriel.

III - Une fois déployé dans JBoss Portal

Les deux portlets sont déployés dans JBoss Portal. Pour mes tests, je mets les deux portlets dans une même page l'un en dessous de l'autre. Ce qui donne ceci :

Si l'on clique sur le lien, on observera que uniquement le portletA est rafraîchit. Ce qui donne au final :

IV - Passer un objet en évènement

Et la suite? Et bien la suite c'est d'être capable de transmettre autre chose que des String en évènement. En fait, on voudrait passer des objets. Il n'y a pas grand chose à faire. Il suffit de respecter les deux points suivants :

- Que la classe des objets à passer en évènement soit serializable.

- Que la classe soit annotée en utilisant l'annotation @XmlRootElement de JAXB .

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

@XmlRootElement
public class User implements Serializable
{
}

Lien pour récupérer la lib JAXB.

Bien sur, il faut toujours respecter ce qui est dit précédement, à savoir implémenter la méthode sendEvent d'un côté, la méthode processEvent de l'autre et déclarer l'évènement dans portlet.xml.

V - A lire

Un ensemble de cours sur developpez au sujet des portlets. Le portail de discussion sur les portlets et les conteneurs de portlets de developpez.

VI - Remerciements

Je tiens à remercier Kraft et Fabien qui m'accompagnent dans la rédaction de la plupart de mes tutoriels. Ce sont mes premiers testeurs. Ainsi que la communauté Developpez pour les relectures et corrections d'ortographe, en particulier : Baptiste Wicht, furr, jacques_jean, pottiez, caro-line. Je remercie tout particuliérement Ricky81 pour m'avoir proposé de devenir rédac Developpez et m'avoir encouragé à aller au bout de la rédaction de ce tutoriel.