Archive pour la catégorie ‘web’

IPC ou communication inter portlet 2. JSR286

Vendredi 22 janvier 2010

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 :

package tuto.portlet;

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 :

package tuto.portlet;

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 :

capture d'écran des portlets du tuto ipc dans JBoss

capture d'écran des portlets du tuto ipc dans JBoss

Une fois cliqué sur le lien “Envoi de l’event” :

capture d'écran des portlets du tuto dans JBoss une fois event envoyé

capture d'écran des portlets du tuto dans JBoss une fois event envoyé

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.

Tweets à chaque nouvel article sur wordpress

Lundi 30 novembre 2009

Ca y est ça marche. Après de nombreux tests j’ai enfin trouvé comment générer automatiquement un post sur twitter (un tweet si vous préférez) à chaque nouvel article publié sur mon blog.

Enfin je veux dire, comment le faire en modifiant le php de wordpress moi-même, sans ajouter un plugin.

Donc je vous livre ce micro-tutoriel Do it yourself

Tout d’abord, pour poster sur Twitter en php, vous allez avoir recours aux fonctions curl. Et pour cela, il faut que votre serveur ait la lib php-curl d’installée.

php5-curl pour php5 bien sur. Sous linux vous pouvez l’ajouter via le gestionnaire de paquets.

Pensez aussi à redémarrer votre serveur Apache.

Ensuite, il faut ajouter dans le fichier wordpress/wp-includes/post.php à la ligne 1536 dans la fonction wp_insert_post() les quelques lignes suivantes :

if($post_type==’post’)
{

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL , “http://twitter.com/statuses/update.xml”);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1);
curl_setopt($ch, CURLOPT_POST , 1);
$title = $post_title;
$url = ‘www.natoine.fr/wordpress/?p=’.$post_ID;
curl_setopt($ch, CURLOPT_POSTFIELDS , ’status=Nouveau post sur mon blog : ‘.$title.’ : ‘.$url);
curl_setopt($ch, CURLOPT_USERPWD , “user:password”);
if ($post_status == ‘publish’ && $previous_status==’draft’) { $buffer_twitter = curl_exec($ch); }
curl_close($ch);
}

Pour moi ça tombe juste après deux appels à do_action() dans post.php et juste avant le return $post_id.
Bien sur il faut remplacer user par votre nom d’utilisateur twitter et password par votre mot de passe twitter.
J’ai fait le choix d’afficher ce message contenant un lien vers le nouveau post du coup, mais vous pouvez mettre ce que vous voulez.

Bon ce tuto est pas super rédigé mais c’est implémenté, tapé ce soir et je ne voulais pas y passer plus de temps.