Push Java avec cometD




 

 
Je termine la série sur le push Java avec cometD en vous montrant par l’exemple comment l’utiliser. Nous reprenons les concepts décrits dans les articles Les clés pour comprendre le push avec cometd et Les concepts de cometD et les mettons en œuvre à l’aide d’un exemple. Autonome sur cometD vous serez, après lecture de cet article.

 

1 – Prérequis

– Maven installé;
– savoir ce qu’est Javascript;
– en avoir fait un peu;
– les bases de JQuery;
– un serveur Tomcat sur lequel déployer notre projet Java;
– avoir lu les articles Les clés pour comprendre le push avec cometd et Les concepts de cometD.

 

2 – Bref rappel sur le push

Le protocole http est une communication réseau entre un client et un serveur. Cependant ce protocole a un défaut : la communication est toujours à l’initiative du client : le serveur ne peut diffuser d’information de sa propre initiative.

Illustration d’une communication via http :
– le navigateur (le client) demande une page web par exemple http://www.developper-jeux-video.com;
– le serveur http (le serveur) lui envoie la page web demandée.

Le principe du push http a donc été conçu pour pallier à cette problématique. C’est bien entendu un contournement et il existe plusieurs manières de le faire. Je vous invite à lire l’article Push PHP en 15 lignes de code pour en voir différentes implémentations.

 

3 – Le push avec cometD

CometD est une api permettant de faire du push sur un format souscription/publication. Le client s’abonne et reçoit tous les messages relatifs à son abonnement.

L’intérêt de cometD est qu’il utilise la meilleure stratégie disponible sur le navigateur pour faire du Push : pour un navigateur ne supportant que HTML4 il pourra utiliser du long polling et pour un navigateur supportant HTML5, les websockets. Les websockets assurant le support natif du push de la norme HTML5.

Une migration HTML4 vers HTML5 ne devrait poser aucun problème pour les outils utilisant l’api cometD.

 

4 – Notre exemple cometD

Nous allons simplement afficher une page web contenant un afficheur d’heure qui sera automatiquement mise à jour par le serveur.

Le développement va comprendre 2 parties :
– la partie cliente en Javascript qui s’abonne au service;
– la partie serveur en Java qui est le service en tant que tel.

 

5 – Initialisation de notre projet cometD

 

5.1 – Arborescence du projet cometD

Notre projet est construit sur la base maven :
– nous créons un dossier pour votre projet que nous nommons cometDDate;
– dans ce dossier créez les dossiers src/main/java, src/main/webapp et src/main/webapp/WEB-INF.

Le dossier src/main/java contiendra les classes relatives à la partie serveur de l’application.
Le dossier src/main/webapp contiendra les fichiers relatifs à la partie cliente de l’application.

Sous linux pour créer une arborescence de répertoires :

mkdir -p src/main/java

 

5.2 – Déclaration des dépendances Maven avec le fichier maven pom.xml

L’artefactId reprend le nom du projet : cometDDate, et le groupId notre arborescence racine de nos packages : cometd.date.

La balise dependencies liste les dépendances dont le projet a besoin : cometd java, cometd javascript et servlet.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cometd.date</groupId>
    <artifactId>cometDDate</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.cometd.java</groupId>
            <artifactId>bayeux-api</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.cometd.javascript</groupId>
            <artifactId>cometd-javascript-jquery</artifactId>
            <version>2.4.3</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>org.cometd.java</groupId>
            <artifactId>cometd-java-server</artifactId>
            <version>2.4.3</version>
        </dependency>
    </dependencies>
</project>

Nous exécutons la commande suivante pour télécharger les dépendances :

mvn eclipse:clean eclipse:eclipse

 

5.3 – Le fichier web.xml nécessaire à notre application web

Nous créons dans le dossier /etc/main/webapp/WEB-INF, un fichier web.xml dans lequel il vous faut déclarer la servlet cometd et la servlet d’initialisation.

La servlet d’initialisation que nous nommons BayeuxInitializer sera chargée d’instancier les services.

 

Source d’erreur, veillez à déclarer correctement cette classe dans la balise servlet-class : vous devez spécifier le nom de la classe ainsi que les packages auxquels elle appartient.

 
Telle que déclarée ci-dessous, la classe BayeuxInitializer appartient au package cometd.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>cometd</servlet-name>
        <servlet-class>org.cometd.server.CometdServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>cometd</servlet-name>
        <url-pattern>/cometd/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>initializer</servlet-name>
        <servlet-class>cometd.BayeuxInitializer</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
</web-app>

 

5.4 – Les packages de notre application

Nous créons les packages :
cometd qui contiendra notre classe BayeuxInitializer;
cometd.date qui contiendra les classes relatives à notre service date.

 

5.5 – Intégration des librairies Javascript nécessaires au projet

Dans le dossier src/main/webapp, nous créons le dossier js qui doit contenir toutes les librairies Javascript nécessaires et qui vous pouvez télécharger ici : Librairie Javascript CometD. Il suffit de décompresser le contenu de l’archive (le fichier zip) dans le dossier webapp.

 

6 – Le développement de notre exemple cometD

L’exemple consiste à développer une partie serveur sur laquelle il est possible de s’abonner et une partie cliente qui est l’abonné. Pour éviter d’avoir à déployer 2 applications web, l’exemple intègre les 2 à la fois le serveur et le client.

 

6.1 – La partie serveur de cometD

6.1.1 – Le cadencement de la mise à jour de l’heure

La diffusion cadencée de l’heure (toutes les secondes) utilise un Timer que nous nommons DateTaskTimer.

Comme tout Timer, cette classe hérite de la classe abstraite TimerTask. Elle oblige donc l’implémentation de la méthode run, méthode chargée de diffuser le message et qui sera appelée toutes les secondes.

La classe DateTaskTimer lit l’heure système, la formate puis la stocke dans une HashMap output. Cette Hashmap est utilisée par cometD pour la diffusion du message qui, dans le cas présent, est l’heure.

La diffusion, quant à elle, utilise la classe cometD ServerSession et sa méthode deliver.

Nous créons donc la classe DateTaskTimer, dans le package cometd.date.

Le code source de la classe DateTaskTimer :

package cometd.date;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;

import org.cometd.bayeux.server.ServerSession;

public class DateTaskTimer extends TimerTask {

  private ServerSession serverSession;
  private Map<String, Object> output;

  public DateTaskTimer(ServerSession remote) {
    super();
    this.serverSession = remote;
  }
	
  @Override
  public void run() {
    output = new HashMap<String, Object>();
    output.put("heure", getDateHeure() );
    serverSession.deliver(serverSession, "/date", output, null);
  }
    
  private String getDateHeure() {
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
    return df.format(Calendar.getInstance().getTime());
  }
}

 

6.1.2 – Le service cometD

Toujours dans le package cometd.date, nous créons la classe DateService, le service chargé de diffuser la date toutes les secondes.

Cette classe :
– hérite de la classe cometd AbstractService;
– crée, par le biais du constructeur, le service à proprement parler pour permette aux clients de s’y abonner;
– intègre la méthode processDate qui est appelée pour la diffusion du message (l’heure) toutes les secondes.

La création du service se fait :
– en invoquant le constructeur de la classe parente qui prend en paramètres un objet BayeuxServer, et le nom du service nommée ici date;
– via la méthode addService qui prend en premier paramètre le chemin d’accès au service /service/date et en second paramètre le nom de la méthode processDate appelée pour la diffusion du méssage.

Le constructeur de la classe DateService :

public DateService(BayeuxServer bayeux)
{
  super(bayeux, "date");
  addService("/service/date", "processDate");
}

La méthode processDate utilise la classe DateTaskTimer décrite plus haut dans un Timer chargé de cadencer toutes les secondes l’appel à la méthode run de la classe DateTaskTimer.

Le cadencement se fait via la méthode schedule de la classe Timer.

Le code source complet de la classe DateService :

package cometd.date;

import java.util.Timer;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.AbstractService;

public class DateService extends AbstractService
{
    public DateService(BayeuxServer bayeux)
    {
        super(bayeux, "date");
        addService("/service/date", "processDate");
    }

    public void processDate(ServerSession remote, Message message)
    {
		Timer t = new Timer();
        DateTaskTimer dateTaskTimer = new DateTaskTimer(remote);
		t.schedule(dateTaskTimer, 0, 1000);
    }
}

Le développement de la partie serveur de notre application est terminée. Il ne reste plus qu’à implémenter la partie cliente.

 

6.2 – La partie cliente de cometD

6.2.1 – La page Web de l’exemple

Ci-dessous le code source html de base de notre afficheur, il intègre :
– les dépendances Javascript cometD;
– une balise div (id=heure) dans laquelle sera affichée l’heure;
– une balise div (id=etatConnexion) dans laquelle sera affichée l’état de la connexion.

Le code source du fichier cometd.jsp :

<html>
 <head>
  <script type="text/javascript" src="./js/jquery/jquery-1.7.2.js"></script>
  <script type="text/javascript" src="./js/jquery/json2.js"></script>
  <script type="text/javascript" src="./js/cometd.js"></script>
  <script type="text/javascript" src="./js/jquery/jquery.cometd.js"></script>
 </head>
 <body>
  <h1>DEMO COMETD</h1>
  <div id="etatConnexion">------</div>  
  <div id="heure">------</div>
 </body>
</html>

 

6.2.2 – Le code Javascript de l’exemple

Maintenant, nous créons une fichier applicationDate.js placé dans le dossier js de notre webapp. Ce fichier va intégrer la partie cliente cometd de notre application.

Dans ce fichier nous instancions un objet cometd et le configurons avec son url d’accès au serveur cometD.

La webapp étant déployée en local, l’adresse du serveur cometd prend la forme : http://localhost:8080/Nom_du_contexte/cometd.

Avec Nom_du_contexte qui reprend :
– le nom de l’artifactId spécifié dans le fichier pom.xml;
– suivi d’un tiret « -« ;
– suivi de la version spécifiée dans le fichier pom.xml.

<artifactId>CometD</artifactId>
...
<version>1.0-SNAPSHOT</version>

Dans le cas présent le nom du contexte est : CometD-1.0-SNAPSHOT (artifactId-version)

var cometd = $.cometd;
var cometURL = "http://localhost:8080/CometD-1.0-SNAPSHOT/cometd";
cometd.configure({
  url: cometURL
});

JQuery met à disposition une fonction exécutée lorsque la page est complètement chargée. Si vous cherchez à accéder à un élément de la page alors que celle-ci n’est pas complètement chargée, Javascript pourrait vous retourner un undefined sur l’élement puisqu’il n’est pas encore chargé.

Tout code exécuté à l’intérieur de la fonction ready l’est uniquement lorsque la page est complètement chargée. Notre code sera donc encapsulé dans cette fonction :

$(document).ready(function() {
...
}
6.2.2.1 – La mise à jour de l’heure

Maintenant, nous définissons 2 variables divEtatConnexion et divHeure qui pointent vers les balises div que nous avons défini dans notre page web. Elles vont nous permettre de mettre à jour le contenu des div correspondants :

var divEtatConnexion = $('#etatConnexion'); 
var divHeure = $('#heure');	

Ensuite nous définissons une fonction metaConnect indicatrice de l’état de la connexion et invoquée sur l’événement /meta/connect comme suit :

  cometd.addListener('/meta/connect', metaConnect);

Le code source de la fonction metaConnect:

var connected = false;
function metaConnect(message)
{
  if (cometd.isDisconnected())
  {
    connected = false;
    connectionClosed();
    return;
  }

  var wasConnected = _connected;
  connected = message.successful === true;
  if (!wasConnected && _connected)
  {
    connectionEstablished();
  } else if (wasConnected && !connected)
  {
    connectionBroken();
  }
}

Cette fonction fait appel à 3 autres fonctions dédiées à la mise à jour de l’affichage de l’état de la connexion dans la la balise div dédiée (etatConnexion) :
connectionEstablished() lorsque la connexion est établie;
connectionBroken() lorsque la connexion est cassée;
connectionClosed() lorsque la connexion est fermée.

Le code source des 3 fonctions :

function connectionEstablished() 
{
  divEtatConnexion.html("CometD connexion OK");
}
function connectionBroken()	
{
  divEtatConnexion.html("CometD connexion perdue");
}
function connectionClosed()	
{
  divEtatConnexion.html("CometD connexion ferm&eacute;e");
}
6.2.2.2 – L’abonnement au service cometD

L’initialisation de la connexion se fait au démarrage du client par un appel à cometD via la méthode handshake :

cometd.handshake();

Cette méthode effectue 2 tâches :
– le client et le serveur se synchronise sur le mode de transport utilisé;
– la tâche précédente réussie, le serveur informe le client des détails lié à la connexion;
– génère un événement handshake.

Le handshake étant asynchrone, le client doit être à l’écoute de l’événement /meta/handshake qui lui indique que la phase de négociation s’est correctement déroulée. Dans ce cas, une fonction callback intégrant l’abonnement du client et la communication est appelée.

Pour s’abonner au service cometD, il suffit d’invoquer la méthode subscribe de cometD, en lui spécifiant le service et la méthode de callback (exécutée à chaque réception d’un message du service).

Dans le cas présent, la fonction met à jour le div dédié à l’affichage de l’heure :

cometd.subscribe('/date', function(message)
{
  divHeure.html(message.data.heure);
});

Avec le code précédent, l’abonnement est fait mais n’est pas démarré, le client doit envoyer un message au service pour indiquer le démarrage de l’abonnement :

cometd.publish('/service/date', "");

Le meta handshake :

cometd.addListener('/meta/handshake', metaHandshake); // écoute de l'événement handshake
function metaHandshake(handshake)
{
  if (handshake.successful === true)
  {
    cometd.batch(function()
    {
      // abonnement au service
      cometd.subscribe('/date', function(message)
      {
        // mise à jour de l'heure dans le div heure
        divHeure.html(message.data.heure);
      });
      // démarrage de la connexion
      cometd.publish('/service/date', "");
    });
  }
}

 

6.3 – Une dernière petite touche

Dans la page web exemple, n’oubliez pas de rajouter la référence au source Javascript :

<script type="text/javascript" src="./js/applicationDate.js"></script>

Et dans le fichier source applicationDate.js, rajoutez la déconnexion à cometD lorsque l’utilisateur quitte la page (événement unload) :

$(window).unload(function() {
  cometd.disconnect(true);
});

 

7 – Les sources

Les sources de l’exemple sont téléchargeables ici : cometD-DevJava.

 

Ceci est un exemple complet sur lequel vous pouvez vous baser pour vos propres développements. Des questions, des précisions à apporter ? Faites m’en part.

 

Posté dans html5, javaTaggé cometd, long polling, polling, protocole de bayeux, push, push http, push java, serveur push, websocket  |  1 commentaire

Une réponse à "Push Java avec cometD"

Répondre

*