Odelia>Technologiesbeta

JXTA On Web

| | | |

Introduction

Cet article décrit l'intégration du framework P2P JXTA dans une application web J2EE. Cela permet d'écrire des applications à base d'interfaces web qui participent au réseau virtuel JXTA.
Comme exemple d'application, nous montrerons comment mettre en oeuvre un système souple de distribution et d'exécution de code distant basé sur le langage dynamique Groovy ! Nous utiliserons également un client riche (RIA) développé avec le projet OpenLaszlo.

Le pont JXTA-Web

Dans son billet Mating Tomcat to JXTA, Mark S. Petrovic, fournit le code utilitaire nécessaire, sous la forme d'une servlet, pour intégrer JXTA dans une application Web Java.
Selon les indications de l'auteur, la servlet BridgeServlet doit être configurée classiquement dans le fichier web.xml de l'application web. Au démarrage du conteneur web, la méthode init() de la servlet assure la configuration et le démarrage de la plateforme JXTA. Dans ce processus, un nouveau pair est créé et celui-ci rejoint un groupe de pairs spécifique nommé "Tomcat Bridge Group" défini par le fichier externe pgadv.xml.
Ce fichier externe XML est une "annonce" de groupe de pairs définissant précisément le groupe que viendront rejoindre en particulier tous les pairs instanciés au travers de la servlet. Nous verrons par la suite que l'un des buts est de permettre une communication entre les pairs de ce groupe.

Le code de la méthode init() démarre également un thread qui, toutes les 30 secondes, va déclencher une requête distante de découverte de nouveaux pairs présents dans le groupe "Tomcat Bridge Group".

Pour rappel, le framework JXTA utilise la notion d'annonce qui sert à décrire une ressource dans le réseau virtuel JXTA formé de pairs, qui s'organise de manière logique en groupe. Il existe de nombreux types d'annonce : un pour le pair, un pour le groupe, un autre définissant un service disponible dans un groupe, ou encore un autre décrivant une sorte de canal par lequel un service peut être sollicité, etc.

La servlet BridgeServlet permet aussi l'affichage, dans une page web, de la liste des pairs qui auront été découverts (en réponse aux requêtes HTTP GET et POST via l'URL de la servlet).

L'application web JXTA-Web

JavaServer Faces comme framework web

Nous avons développé l'application web JXTA-Web, comportant la servlet BridgeServlet, avec la version gratuite de l'outil d'Exalead basé sur l'IDE Eclipse 3.2. Il s'agit d'une application web utilisant le framework JXTA 2.4, l'API JavaServer Faces et le projet facelets. Mais rien n'empêche d'utiliser un autre framework web, comme par exemple Struts ou Spring MVC.

Comment accéder aux services de base de JXTA depuis le reste de l'application web ? Tout simplement en positionnant dans le contexte de la servlet un attribut ayant comme nom "pg" et comme valeur, la référence de type PeerGroup du groupe de pair qui a été instancié par la servlet BridgeServlet.
Dans le cas de JavaServer Faces, cette référence s'obtient par la suite en accédant au contexte externe qui est un contexte de servlet.
Voici le code, dans le cas de JSF, qui permet de récupérer l'objet PeerGroup placé en attribut dans ce contexte lors de l'initialisation de la servlet :

ServletContext context = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
group = (PeerGroup) context.getAttribute("pg");

Ce code peut par exemple être exécuté dans une méthode de "backing bean" JSF.

L'application JXTA-Web

Avec l'application web JXTA-Web comprenant la servlet BridgeServlet discutée plus haut, nous avons développé un service JXTA chargé d'exécuter tout script Groovy qui lui est transmis par un autre pair. Ce service, appelé GroovyJXTAService, est démarré et publié dans le groupe "Tomcat Bridge Group" lors de l'initialisation de la servlet.
L'interface web de l'application va permettre de présenter à l'internaute tous les services JXTA de type GroovyJXTAService s'exécutant dans le groupe spécifié du réseau virtuel JXTA.
L'utilisateur pourra alors sélectionner un service particulier, saisir du code Groovy dans une zone d'édition, puis transmettre ce code pour exécution au service cible. Si le code Groovy renvoie une valeur de retour, celle-ci est affichée dans la page web juste en dessous de la zone d'édition contenant la requête.

Voici un exemple très simple de script Groovy :

return System.getProperties().toString();

Ce script va tout simplement permettre de renvoyer les propriétés système de la machine qui exécute le pair et le service cible.
Notez au passage que ce script fait appel à la classe Java System.

Cliquez ici pour avoir un aperçu de l'interface web de JXTA-Web.

Nous avons donc un système souple de distribution et d'exécution de code distant basé sur le langage dynamique Groovy !

Quant à l'aspect sécurité c'est un sujet conséquent dans le framework JXTA et il serait préférable pour un tel type d'application d'employer un groupe de pair sécurisé.


Service JXTA d'exécution de code Groovy

Voici quelques détails techniques concernant le service JXTA GroovyJXTAService.
La définition d'un service JXTA est donnée dans le guide du programmeur "JXTA v2.3.x: JavaT Programmer's Guide" téléchargeable depuis le site JXTA, au chapitre 7.

Le service GroovyJXTAService est publié dans le groupe par une annonce de type ModuleSpecAdvertisement qui inclut elle-même une annonce de pipe ; ce pipe, qui est une sorte de canal, pourra être utilisé par un pair pour invoquer le service. Deux autres annonces sont aussi associées au service : l'annonce ModuleClassAdvertisement (la classe du service) et l'annonce ModuleImplAdvertisement (définit une implémentation de la spécification).

Chaque élément de la liste des services présentée à l'internaute dans l'application JXTA-Web correspond à une annonce ModuleSpecAdvertisement. Les informations présentées sont la description, la version, l'auteur, et la description du pipe.
Les trois premières informations proviennent des propriétés de l'annonce ModuleSpecAdvertisement, et la quatrième vient de la description de l'annonce du pipe associée.
Il fallait un moyen d'identifier la provenance de chaque service : ce que nous avons retenu c'est de faire apparaître le nom de la machine qui exécute le service, sous la forme " GroovyJXTAService sur ". Cette information est placée dans la description du pipe.
Le nom de la machine est obtenue par un appel à :

InetAddress.getLocalHost().getHostName()

au moment où l'annonce du pipe est générée.

Le service GroovyJXTAService s'appuie sur la classe JXTA net.jxta.util.JxtaServerPipe pour être à l'écoute de messages entrant contenant le script Groovy à exécuter, sur son pipe. Et lorsque le service accepte un message entrant, une connexion bidirectionnelle est mise en place au travers d'un objet de la classe JXTA net.jxta.util.JxtaBiDiPipe.

Pour exécuter du script Groovy il y a plusieurs méthodes possibles (voir Embedding Groovy). Nous avons utilisé la classe Groovy GroovyShell avec un appel à sa méthode evaluate en passant le script à exécuter :

GroovyShell shell = new GroovyShell();
Object result = shell.evaluate(script);

Le résultat de l'évaluation est alors retourné dans un message JXTA (net.jxta.endpoint.Message) transmis au pair qui a établi la connexion bidirectionnelle.


Le "backing bean" ServicesJXTA

ServicesJXTA est le nom de la classe défini comme "backing bean" nommé "services". Ce bean est utilisé dans la page web de l'application JXTA-Web qui permet d'interagir avec les services GroovyJXTAService.

La liste des services est affichée au moyen d'un élément h:dataTable dont l'attribut value vaut #{services.advertisements}. Autrement dit la méthode ServicesJXTA.getAdvertisements() est appelée pour fournir au composant d'affichage la liste des objets à présenter.
Cette méthode renvoie une liste d'objets ModuleSpecAdvertisement obtenus depuis le cache local des annonces, de la manière suivante :

public List getAdvertisements() throws IOException {
  // Recherche des annonces de spécification de module du service
  DiscoveryService discovery = group.getDiscoveryService();

  // Rechercher dans le cache local
  Enumeration enumAdv = discovery.getLocalAdvertisements(DiscoveryService.ADV, "Name", "JXTASPEC:GroovyJXTAService");
  return Collections.list(enumAdv);
}

Ici group est la référence d'objet de type PeerGroup qui a été obtenue depuis le contexte de la servlet.
Cette méthode récupère donc depuis le cache local toutes les annonces de spécifications de module du service GroovyJXTAService. Il faut bien sûr avoir au préalable lancé une recherche distante de ce type d'annonce, par un appel du genre :

discovery.getRemoteAdvertisements(null, DiscoveryService.ADV, "Name", "JXTASPEC:GroovyJXTAService", 10);

Ainsi, chaque annonce découverte par cette recherche est placée dans le cache local du pair. On pourrait par exemple, au démarrage de la servlet JXTABridge, démarrer un thread chargé d'exécuter à intervalle de temps régulier cette recherche, ou encore effectuer cette recherche à la demande de l'utilisateur.

Après avoir choisi un service particulier, en sélectionnant le bouton radio correspondant, puis rempli la zone d'édition appropriée avec du script Groovy, l'utilisateur déclenche l'envoi du script vers le service désigné, par le bouton "Exécuter".
Techniquement, à ce moment là, une méthode du "backing bean" est appelée ; celle-ci récupère la référence du ModuleSpecAdvertisement sélectionné, et à partir de celle-ci (et avec la référence du groupe) est en mesure de créer un objet de type net.jxta.util.JxtaBiDiPipe pour établir une connexion bidirectionnelle avec le service.
La méthode envoie ensuite un message JXTA comprenant le script au service, et en attend la réponse. Le résultat de la requête est alors affiché dans la page web.


Ajouter un client riche avec OpenLaszlo

Bien que cela ne soit pas le but principal de cet article, nous allons montrer comment intégrer un client Internet riche à l'aide du projet open source OpenLaszlo.

En résumé, OpenLaszlo est un projet permettant de créer des applications Internet riches (RIA) avec un langage orienté objet proche du langage XML dans sa forme, appelé LZX, et utilisant le langage JavaScript pour le codage.
Techniquement, OpenLaszlo se présente sous la forme d'une servlet Java (le serveur OpenLaszlo) capable de compiler et de générer des fichiers flash .swf à partir de fichiers source .LZX.

Nous pouvons remplacer la page principale de l'application web présentée plus haut (qui est une page JSF), par une page web contenant un objet flash qui réalise les mêmes fonctionnalités : affichage de la liste des services GroovyJXTAService, sélection d'un service particulier, saisie de code Groovy, puis soumission de ce code à la partie serveur pour exécution ; une légère différence avec la page JSF est que le résultat de l'exécution du code Groovy par le service distant est affiché dans une boîte de dialogue OpenLaszlo.

Cliquez ici pour avoir un aperçu de l'interface OpenLaszlo de JXTA-Web.

L'une des grandes forces du projet OpenLaszlo est à mon avis le mode d'interaction entre client riche et serveur : du fait de l'utilisation d'XML, un client OpenLaszlo peut s'interfacer avec un grand nombre de technologies serveur Java (JSP, JSF, Cocoon,.), PHP, ASP.NET, etc.

Nous avons donc développé une application OpenLaszlo que nous avons déployée en mode SOLO ; c'est un mode dans lequel on utilise le fichier .swf résultant de la compilation d'un ou de plusieurs sources LZX, et qui est déployé dans une application web sans le serveur OpenLaszlo qui est une servlet.

Le client flash OpenLaszlo obtient la liste des services GroovyJXTAService au moyen d'un ensemble de données (balise dataset OpenLaszlo) obtenu par requête HTTP, pour l'afficher dans la grille (balise grid). L'URL utilisée pour obtenir les données correspond tout simplement à une vue JSF écrite pour générer un document XML détaillant les services, utilisant le "backing bean".

Le client OpenLaszlo contient également l'équivalent d'un formulaire HTML capable de soumettre le choix de service GroovyJXTAService et la saisie du script Groovy (balise form) en utilisant une URL précise.
Avec JSF, la difficulté est alors d'effectuer le traitement qui, ici, consiste à invoquer le service cible, puis ensuite à générer un message XML contenant la réponse à afficher dans le client flash.
L'approche que nous avons retenue a été de développer une servlet, appelé AjaxServlet, dédiée à cette tâche.
D'autre part, afin de réutiliser le "backing bean" JSF, il fallait pouvoir appeler celui-ci à partir de la servlet ; ce qui peut être fait avec le code suivant :

ServicesJXTA services = (ServicesJXTA) getServletContext().getAttribute("services");

Dans ce bout de code, "services" est le nom du bean géré de type ServicesJXTA (c'est notre " backing bean ") déclaré dans le fichier de configuration JSF faces-config.xml. Comme vous pouvez le constater, ce bean est accessible comme attribut du contexte de la servlet.
Il n'a pas été nécessaire de le placer explicitement dans ce contexte, du fait de sa visibilité de type application définie dans le fichier faces-config.xml (balise managed-bean-scope).

Le fait de pouvoir solliciter des données de manière asynchrone à partir du client riche OpenLaszlo et de les interpréter, correspond bien à de l'AJAX ; d'où le nom de la servlet !


Améliorations possibles

Bien sûr l'aspect distribution de l'application JXTA-Web peut aller beaucoup plus loin : il serait tout à fait possible de distribuer un script Groovy qui soit enregistré sous la forme de fichier sur la machine qui exécute le pair distant, et de déclencher l'exécution de ce script simplement en transmettant le nom du fichier.

Un autre point de l'application JXTA-Web qui pourrait évoluer, est que nous avons utilisé un modèle requête/réponse de type synchrone : le pair émettant la requête attend la réponse du service s'exécutant sur le pair distant. Or JXTA fonctionne en mode asynchrone par échange de messages ; on peut imaginer des situations où l'on exécute sur un pair distant une application Swing construite avec Groovy et qui soumet ensuite les saisies d'un utilisateur au pair appelant...

balises dans Langages et systèmes

AJAX Camel DSL Grails Groovy GSP Java JBI prefuse RSS ServiceMix SOA