Dans cet article, nous montrerons comment l'agilité du langage Groovy permet d'écrire plus facilement des services P2P pour le framework JXTA et son implémentation Java (JXSE, version 2.5).
Nous partirons d'un service générique écrit en langage Groovy, dont une instance pourra être démarrée au sein d'un groupe de pair JXTA, et à laquelle sera transmis un script Groovy constituant son essence. Puis nous monterons comment invoquer ce service à partir d'un pair quelconque du réseau virtuel JXTA.
Un service générique Groovy
Le framework JXTA définit la notion de service dont on peut trouver un exemple dans le guide JXTA JavaTM Standard Edition v2.5: Programmers Guide du projet JXTA, ainsi que dans le récent ouvrage « Practical JXTA » de Jérôme Verstrynge.
Un service JXTA a recours à un Pipe bidirectionnel, qui est une sorte de canal, pour communiquer avec les autres pairs dans un groupe de pair défini. Comme nous le verrons, des objets de type net.jxta.endpoint.Message pourront être échangés sur ce canal.
Notre service générique est implémenté par la classe Groovy GroovyJXTAService qui implémente elle même l'interface JXTA net.jxta.service.Service.
Le code source de cette classe est contenu dans le fichier GroovyJXTAService.groovy disponible en pièce jointe à cet article.
La librairie contenant ce service est aussi disponible à l'adresse http://www.odelia-technologies.com/jxgroovy/jxgroovy-1.0-SNAPSHOT.jar ; d'ailleurs cette URL est reprise dans l'annonce d'implémentation de module (net.jxta.protocol.ModuleImplAdvertisement) définie par le service.
Cette annonce indique la disponibilité de l'implémentation du service JXTA GroovyJXTAService, en précisant en particulier le nom de la classe qui implémente le service, ainsi que l'URI de la librairie qui la contient ; elle fait aussi le lien avec l'annonce de spécification de module (net.jxta.protocol.ModuleSpecAdvertisement), qui peut être récupérée, dans notre exemple, via l'URL http://www.odelia-technologies.com/jxgroovy/MSA.xml.
Cette dernière est très importante pour permettre à un pair client d'utiliser le service, parce qu'elle contient l'annonce du Pipe qui permettra une communication bidirectionnelle avec le service.
Ainsi, un pair JXTA s'exécutant sur une JVM pourra, au travers d'un groupe de pair, charger et démarrer le service, à partir de l'annonce d'implémentation du service. Un objet de type GroovyJXTAService sera alors instancié à partir de la libraire jxgroovy-1.0-SNAPSHOT.jar récupérée, dans notre cas, depuis Internet.
La classe utilitaire GroovyPeer
La classe Groovy GroovyPeer est une simple classe utilitaire facilitant le démarrage de la plateforme JXTA ; le constructeur prend quatre arguments, respectivement : le nom du pair, le type de pair (sous la forme d'une chaîne de caractères convertie en une valeur de l'énumération NetworkManager.ConfigMode), un chemin indiquant le répertoire de configuration, et enfin, une Closure Groovy, qui sera appelée automatiquement par le constructeur, et dont les instructions seront résolues avec la référence vers une instance de type net.jxta.platform.NetworkConfigurator.
Voici un exemple d'utilisation de la classe GroovyPeer, en Groovy, qui se termine par le démarrage de la plateforme JXTA :
tcpPort = 9745
tcpEnabled = true
tcpIncoming = true
tcpOutgoing = true
…
save() // enregistrer la configuration
}
groovyPeer.startNetwork()
Après l'appel à la méthode startNetwork(), la propriété netPeerGroup de la classe GroovyPeer, est initialisée avec la référence vers le groupe de pair NetPeerGroup.
Le code source de la classe GroovyPeer est également disponible en tant que pièce jointe à cet article.
Démarrer le service GroovyJXTAService dans le groupe NetPeerGroup
Le démarrage du service GroovyJXTAService dans le groupe de pair NetPeerGroup s'effectue en deux étapes : tout d'abord, il faut charger le service, puis ensuite le démarrer.
Nous pouvons effectuer le chargement du module grâce à la méthode PeerGroup.loadModule (un service JXTA est un module) en transmettant l'ID de classe du service, et l'annonce de son implémentation ; ces deux informations s'obtiennent à partir de la classe GroovyJXTAService :
Vient ensuite le démarrage du service :
Ici, nous démarrons le service en passant un paramètre de type String[] au moyen de l'opérateur Groovy as (une liste de chaînes de caractères transtypée en String[]) ; comme le code le montre, le tableau transmis ne comporte qu'un seul élément qui est une chaîne de caractères contenant du code Groovy.
L'utilisation, ci-dessus, du tripe apostrophe simple comme délimiteur de chaîne de caractères permet d'étendre ce code sur plusieurs lignes pour plus de lisibilité.
A quel moment ce script sera-il exécuté ? A chaque fois que le service recevra un message, après qu'une connexion soit acceptée.
Le service GroovyJXTAService utilise classiquement le tandem de classes JxtaServerPipe/JxtaBiDiPipe pour servir les requêtes vers celui-ci. Une instance du type JxtaServerPipe est constamment à l'écoute de connexions, et lorsque lorsqu'une connexion est acceptée, fournit une instance de type JxtaBiDiPipe grâce à laquelle il est alors possible d'échanger des messages entre le service et un autre pair du réseau JXTA.
C'est au moment où le service détecte l'arrivée d'un message dans le pipe bidirectionnel que le code Groovy transmis au démarrage du service s'exécute.
Techniquement, le code Groovy est compilé et exécuté à l'aide d'un objet de type GroovyShell ; par ailleurs, deux variables particulières sont liées dynamiquement au code Groovy, de sorte que celui-ci peut tout à fait les utiliser : ce sont les variables event et pipe.
La variable event, qui est de type net.jxta.pipe.PipeMsgEvent, permettra en particulier de récupérer le message JXTA reçu dans le pipe. Et la variable pipe, de type net.jxta.util.JxtaBiDiPipe, pourra servir à envoyer des messages à l'autre bout du pipe.
L'exemple ci-dessous en donne une illustration : la variable event permet d'accéder au message JXTA reçu, et de lire la valeur d'un élément du message, tandis que la variable pipe sert à envoyer un message JXTA préalablement construit.
import net.jxta.endpoint.Message
import net.jxta.endpoint.StringMessageElement
println "Salut tout le monde !"
println "message : ${event.message.getMessageElement("ns", "elm")}"
def message = new Message()
def sme = new StringMessageElement("elm", "Salut de service", null)
message.addMessageElement("ns", sme)
pipe.sendMessage(message)
'''
] as String[])
Ecrire un client du service GroovyJXTAService
Voyons maintenant comment écrire le code client capable d'invoquer le service GroovyJXTAService s'exécutant sur un pair JXTA.
Le point essentiel est de posséder, ou du moins d'avoir découvert via le service Discovery JXTA, l'annonce de spécification du service qui contient notamment l'annonce du pipe servant à communiquer avec le service.
Dans notre exemple, cette annonce est disponible sous le forme d'un document XML par l'URL http://www.odelia-technologies.com/jxgroovy/MSA.xml.
Le code Groovy qui suit, permet d'initialiser un objet Advertisement correspondant à ce document :
def url = new URL('http://www.odelia-technologies.com/jxgroovy/MSA.xml')
url.withInputStream { is ->
moduleSpecAdv = AdvertisementFactory.newAdvertisement(MimeMediaType.XMLUTF8, is)
}
L'invocation du service peut maintenant s'effectuer à l'aide d'une instance de la classe JxtaBiDiPipe :
// code Groovy
} as PipeMsgListener)
if (biDiPipe.isBound()) {
def message = new Message()
def sme = new StringMessageElement('elm', 'Salut !', null)
message.addMessageElement('ns', sme)
biDiPipe.sendMessage(message)
sleep 1000
}
L'annonce du pipe du service est tout simplement obtenue par le code moduleSpecAdv.pipeAdvertisement qui équivaut à moduleSpecAdv.getPipeAdvertisement().
Si l'on parvient à établir une connexion avec le service GroovyJXTAService, un message JXTA lui est envoyé, puis il y a une temporisation d'une seconde de manière à permettre la réception d'une réponse de la part du service.
Le code Groovy qui traite la réception d'une éventuelle réponse du service dans le pipe bidirectionnel a été passé dans le constructeur de la classe JxtaBiDiPipe, en dernier paramètre, sous la forme d'une Closure Groovy.
En effet, le langage Groovy permet l'implémentation d'une interface Groovy/Java, ici l'interface JXTA net.jxta.pipe.PipeMsgListener, sous la forme d'une Closure !
Le code client complet, c'est-à-dire celui d'un pair JXTA joignant le groupe de pair NetPeerGroup, puis invoquant le service GroovyJXTAService, peut prendre cette forme :
import net.jxta.pipe.PipeMsgListener
import net.jxta.document.AdvertisementFactory
import net.jxta.document.MimeMediaType
import net.jxta.endpoint.*
def groovyPeer = new GroovyPeer('test', 'EDGE', './config') {
tcpPort = 9746
}
groovyPeer.startNetwork()
def moduleSpecAdv
def url = new URL('http://www.odelia-technologies.com/jxgroovy/MSA.xml')
url.withInputStream { is ->
moduleSpecAdv = AdvertisementFactory.newAdvertisement(MimeMediaType.XMLUTF8, is)
}
// Invocation du service
def biDiPipe = new JxtaBiDiPipe(groovyPeer.netPeerGroup, moduleSpecAdv.pipeAdvertisement, 30000, { event ->
// code Groovy
} as PipeMsgListener)
if (biDiPipe.isBound()) {
def message = new Message()
def sme = new StringMessageElement('elm', 'Salut ! (startMessenger)', null)
message.addMessageElement('ns', sme)
biDiPipe.sendMessage(message)
sleep 1000
biDiPipe.close()
}
groovyPeer.stopNetwork()
| Fichier attaché | Taille |
|---|---|
| GroovyJXTAService.groovy | 4.09 Ko |
| GroovyPeer.groovy | 872 octets |