|
Introduite par Sun en 1998, la technologie Jini simplifie interfaçage et la connexion entre ordinateurs nomades : PC, PDA, téléphone mobile, TV, Console de Jeux, Digital Camera, HiFi, Imprimantes, Fax, Alarmes, GPS, Domotique, … en plus elle fournit un cadre dans lequel, les services peuvent faire connaître leur existence et leur service d’une part et d'autre part, n'importe quel bout de logiciel peut rechercher un service implémentant un ensemble de fonctionnalités données et établir un "contrat" pour l'utiliser.
Architecture de JNI
En Jini un service peut être implémenté en matériel : une imprimante offre un service d'impression, mais aussi par logiciel: un traitement de texte peut être vu comme un service. Dans les deux cas la technologie se base sur le chargement dynamique du code, Cette transmission de données se fait à travers un système de couches, quant aux connexions, elles sont effectuées grâce à un protocole propriétaire JRMP (Java Remote Method Protocol) basé sur TCP/IP.
nous pouvons dire que la technologie Jini est une extension réseau de l’infrastructure, model de programmation et les services de la technologie Java. Comme le motre de tableau ci-dessous:
|
Infrastructure |
Programmation model |
Services |
Base Java |
Java VM
RMI
Java Security |
Java APIs
Java Beans
… |
JNDI
Entreprise Beans
… |
Java + Jini |
Discovery/join
Distributed Security
Lookup |
Leasing
Transcations
Events |
Printing
Transacation Manager
… |
L’infrastructure de JINI
Dans la technologie Jini, nous distinguons trois pilé de l’infrastructure :
- Les classes Java RMI (Remote Method Invocation) qui permettant de manipuler des objets distants (objet qui existe dans un autre espace adresse soit dans la même machine soit dans une machine différente), et qui permet aussi d'assurer la sécurité de l'environnement distribué Jini.
- Le protocole de découverte (discovery process) et d'adhésion (join process) qui permettent respectivement de découvrir un service d’enregistrement et enregistrer un service.
- Le service de recherche (lookup service), qui est le recueil des services existants.
Mécanisme de Jini
Le Service Provider (fournisseur du service) recherche un Lookup Service (service d’enregistrement)

Le Service Provider (fournisseur du service) enregistre l'objet service ainsi que ses attribues dans le Lookup Service (service d’enregistrement)

Le client demande un service par rapport à ses attribues. Une copie de ce dernier lui transmise pour qu’il puisse communique avec le service.

Le client interagi directement avec le Service Provider (fournisseur du service) par l’intermédiaire de l’objet service.

Le modèle de programmation de Jini
La technologie Jini réside sur un système distribué supposé fiable. Ce modèle de programmation propose trois API :
- Leasing : permet de gérer le bail (la durée de vie) des objets distribués en étendant la notion de ramasse-miettes (garbage collector) aux environnements distribués. Le bail se caractérise par:
- Une période le fournisseur du bail garantit au demandeur l’accès à une certaine ressource,
- La durée peut en être définie par le fournisseur ou négociée par les deux parties,
- Pendant la durée du bail, celui-ci peut être annulé par le demandeur. Les ressources en jeu sont alors libérées
- Transaction: un ensemble d’opérations groupées de manière à étendre le principe d’atomicité, selon les critères suivants :
- Elles réussissent ou échouent ensemble.
- Pour un observateur extérieur, elles constituent une unique opération.
En cas de problème chez un des participants d’une transaction, l’ensemble de l’opération échoue, ce qui évite de laisser des entités dans des états intermédiaires. Une transaction est créée et surveillée par un Manager. Un Manager est un objet qui implémente l’interface TransactionManager.
La validation d’une transaction implique le vote de chaque participant. Les trois votes possibles sont :
- Prepared (Prêt), si le participant estime que la transaction, de son point de vue, peut être validée,
- Not changed (i nchangé), si les opérations qui le concernaient étaient en lecture seule,
- Aborted (Annuler), si le participant estime que la transaction ne doit pas être validée.
- Events : défini comme un changement d’état abstrait d’un objet, c’est une prolongation du modèle d'événement employé par des composants de « JavaBeans » pour l'environnement distribué qui permet la communication basée par des événements entre les services de Jini.
Premier programme
Pour réaliser un programme Jini, nous allons procède en plusieurs étapes :
- Définir l'interface pour la classe distante (HelloWord) qui hérite de l'interface Remote et déclarer les méthodes publiques globales de l'objet. De plus ces méthodes doivent relever une exception de type RemoteException
HelloWord.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloWord extends Remote {
public String hello() throws RemoteException;
}
|
- Définir la classe du service (HelloWordImpl) qui implémente l’interface HelloWord et l’interface ServiceIDListener nécessaire pour le « call-back » du JoinManager (Join) quand un identifiant du service est assignée, en générale cette interface est utilisé par des services qui n’ont pas encre d’identifiant.
En plus la classe du service doit hérite de la classe UnicastRemoteObject ou Activatable (utilisant elle-même les classes Socket et SocketServer, permettant la communication par protocole TCP) -dans notre cas on va s’intéresser à UnicastRemoteObject pour débuter – afin de créer un lien au système de RMI et aussi initialisation à distance d'objet. Rappelons que les objets de cette classe doivent être Serializable, un mécanisme qui permet de sérialiser des objets en flux réseau pour faciliter leur transfère.
HelloWordImpl.java
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import net.jini.core.lookup.ServiceID;
import net.jini.lookup.ServiceIDListener;
public class HelloWordImpl extends UnicastRemoteObject implements
HelloWord, ServiceIDListener {
private String message;
private ServiceID serviceID;
public HelloWordImpl(String msg) throws RemoteException {
message = msg;
}
public String hello() throws RemoteException {
System.out.println("Méthode hello est invoquée");
return message;
}
public void serviceIDNotify(ServiceID sidIn) {
serviceID = sidIn;
System.out.println("Objet enregistré sous l'Id: " + sidIn);
}
}
|
N.B : vous remarquerez que la classe HelloWordImpl n’implémente pas Serializable parce qu’elle dispose des propriétés qui sont au préalable Serializable.
- Écrire le Service Provider (ServerUniCast) c’est le programme serveur qui instancie le service.
ServerUniCast.java
import java.rmi.RMISecurityManager;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.entry.Entry;
import net.jini.discovery.LookupLocatorDiscovery;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.JoinManager;
import net.jini.lookup.entry.Name;
import net.jini.lookup.entry.ServiceInfo;
public class ServerUniCast {
public static void main(String[] argv) {
try {
System.setSecurityManager(new RMISecurityManager());
HelloWordImpl hello = new HelloWordImpl("Salut!");
LookupLocator[] llc = new LookupLocator[1];
llc[0] = new LookupLocator("jini://localhost");
Name nameEntry = new Name("HelloWord");
ServiceInfo serviceInfo = new ServiceInfo(,
, ,
, ,
);
Entry aeAttributes[] = new Entry[] { nameEntry, serviceInfo };
new JoinManager(hello, aeAttributes, hello,
new LookupLocatorDiscovery(llc),
new LeaseRenewalManager());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
|
- Ecrire le programme client (ClientUniCast):
ClientUniCast.java
import java.rmi.RMISecurityManager;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.lookup.entry.Name;
public class ClientUniCast {
public static void main(String[] args) {
HelloWord myHello;
try {
System.setSecurityManager(new RMISecurityManager());
System.out.println("Recherche du lookup service");
LookupLocator lookup = new LookupLocator("jini://localhost");
ServiceRegistrar registrar = lookup.getRegistrar();
Name nameEntry = new Name("HelloWord");
Entry aeAttributes[] = new Entry[] { nameEntry };
ServiceTemplate template = new ServiceTemplate(null, null,
aeAttributes);
myHello = (HelloWord) registrar.lookup(template);
System.out.println(myHello.hello());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
|
-
Générer le Stub en utilisant la commande rmic (#rmic Bonjour)
-
Lancer leregistry (rmiregistry) puis le service lookupService en utilisant l'archive start fournit avec Jini.
#java -Djava.security.policy=priv.policy -jar C:\jini2_1\lib\start.jar
start-transient-reggie.config
Avec priv.policy fichier de privilèges et start-transient-reggie.config fichier de configuration du LookupService qui nécessite à son tour le fichier transient-reggie.config
N.B. le LookupService nécessite un serveur web pour qu’il puisse charger les classes de son implémentation. Si vous nous disposer pas d'un serveur vous pouvez utiliser celui fournit par Jini en tapant la commande suivante.
#java -jar C:\jini2_1\lib\tools.jar -port 80 -dir C:\jini2_1\lib –verbose
- lancer le service provider.
#java -Djava.security.policy=priv.policy ServerUniCast
-
exécuter le client
#java -Djava.security.policy=priv.policy ClientUniCast
Diffusion (Multicast)
Dans l’exemple précédant la découverte (discovery) et la recherche (lookup) se faisait on “Unicast “ par ce qu’on connaît le host et/ou le port du lookupservice. Dans le cas contraire ou on désire faire autrement on peut effectuer cette découverte et la recherché en “Multicast“ (diffusion) qui consiste à chercher l’ensemble des lookupservice présent dans un tronçons réseaux.
ServerMultiCast.java
import java.rmi.RMISecurityManager;
import net.jini.core.entry.Entry;
import net.jini.discovery.LookupDiscovery;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.JoinManager;
import net.jini.lookup.entry.Name;
import net.jini.lookup.entry.ServiceInfo;
public class ServerMultiCast {
public static void main(String[] argv) {
try {
System.setSecurityManager(new RMISecurityManager());
HelloWordImpl hello = new HelloWordImpl("Salut!");
Name nameEntry = new Name("Bonjour");
ServiceInfo serviceInfo = new ServiceInfo("Nom du service",
"Fabricant du service", "Fournisseur du service",
"Version du service", "Model du service", "Numero du serie");
Entry aeAttributes[] = new Entry[] { nameEntry, serviceInfo };
LookupDiscovery ld = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
new JoinManager(hello, aeAttributes, hello, ld,
new LeaseRenewalManager());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
|
ClientMultiCast.java
import java.rmi.RMISecurityManager;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscovery;
import net.jini.lookup.entry.Name;
public class ClientMultiCast implements DiscoveryListener {
public void discovered(DiscoveryEvent de) {
HelloWordImpl hello = null;
Name nameEntry = new Name("Bonjour");
Entry aeAttributes[] = new Entry[] { nameEntry };
ServiceTemplate template = new ServiceTemplate(null, null, aeAttributes);
try {
ServiceRegistrar[] registrars = de.getRegistrars();
for (int i = 0; i < registrars.length; ++i) {
hello = (HelloWordImpl) registrars[i].lookup(template);
}
System.out.println(hello.hello());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public void discarded(DiscoveryEvent de) {
}
public static void main(String[] args) {
ClientMultiCast client = new ClientMultiCast();
try {
System.setSecurityManager(new RMISecurityManager());
System.out.println("Recherche du lookup service");
LookupDiscovery lookup = new LookupDiscovery(
LookupDiscovery.ALL_GROUPS);
lookup.addDiscoveryListener(client);
while (true) {
Thread.sleep(1000);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
|
De la même façon que l'exemple de l'unicast, nous exécutons celui la.
LookupServcie
Dans cette partie nous allons voir un exemple d’un programme qui créer dynamiquement un LookupService.(ServerLookup)
ServerLookup.java
import java.rmi.RMISecurityManager;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscovery;
import net.jini.lookup.entry.Name;
public class ServerLookup implements DiscoveryListener {
public void discovered(DiscoveryEvent de) {
try {
System.out.println("Lookup Service Found");
HelloWordImpl hello = new HelloWordImpl("Salut!");
Name nameEntry = new Name("Bonjour");
ServiceInfo serviceInfo = new ServiceInfo("Nom du service",
"Fabricant du service", "Fournisseur du service",
,, );
Entry aeAttributes[] = new Entry[] { nameEntry, serviceInfo };
LookupDiscovery ld = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
new JoinManager(hello, aeAttributes,hello, ld, new LeaseRenewalManager());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public void discarded(DiscoveryEvent de) {
}
public static void main(String[] argv) {
ServerLookup myServer = new ServerLookup();
try {
System.setSecurityManager(new RMISecurityManager());
LookupDiscovery ld = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
ld.addDiscoveryListener(myServer);
LookupServiceThread ls1 = new LookupServiceThread(argv);
ls1.start();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class LookupServiceThread extends Thread {
String[] configfile;
public LookupServiceThread(String[] configfile) {
this.configfile = configfile;
}
public void run() {
try {
LookupServiceCreator lsp = new LookupServiceCreator(configfile);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class LookupServiceCreator implements ServiceDescriptor {
private Object service;
private Configuration config;
public LookupServiceCreator(String[] args) throws Exception {
config = ConfigurationProvider.getInstance(args);
service = create(config);
}
public Object create(Configuration config) throws Exception{
Object c = new Object();
String codebase = null;
String policy = null;
String classpath = null;
String[] serverConfigArgs = null;
ServiceDescriptor desc = null;
try {
codebase = (String) config.getEntry("ServiceDescription",
"codebase", String.class);
policy = (String) config.getEntry("ServiceDescription", "policy",
String.class);
classpath = (String) config.getEntry("ServiceDescription",
"classpath", String.class);
serverConfigArgs = (String[]) config.getEntry("ServiceDescription",
"serverConfigArgs", String[].class);
desc = (ServiceDescriptor) new NonActivatableServiceDescriptor(
codebase, policy, classpath,
"com.sun.jini.reggie.TransientRegistrarImpl",
serverConfigArgs);
c = desc.create(config);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return c;
}
}
|
Le programme ServerLookup va crée un LookupService puis enregistrer l'objet Hello dans ce dernier. Pour cela nous avons besoin d'un fichier de configuration (service.config) qui définit les propriétés d'un lookupservice.
puis nous lancons le programme.
#java -Djava.security.policy=priv.policy ServerLookup service.config
Pour tester en exécutant le ClientMulticast.
#java -Djava.security.policy=priv.policy ClientUniCast
Transaction
Nous pouvons assimiler la validation d’une transaction par une conformité d'option (vote à l'unanimité) ce suffrage implique le vote positif de chaque participant. En cas de problème chez un des participants d’une transaction, l’ensemble de l’opération échoue, ce qui évite de laisser des entités dans des états intermédiaires.
Prenons un exemple où tous les politiciens doivent vote à l'unanimité pour ratifier un texte de loi. Pour cela nous allons définir trois classes Urne qui représente l'urne, Oui et Non qui représentent les bulletin du scrutin puis pour tester nous allons écrire une classe Vote pour voter et dépouiller.
Oui.java
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.transaction.server.TransactionParticipant;
public class Oui extends UnicastRemoteObject implements TransactionParticipant {
public Oui() throws RemoteException {
super();
}
public synchronized void abort(TransactionManager mgr, long transactionID) {
}
public synchronized void commit(TransactionManager mgr, long transactionID) {
}
public int prepare(TransactionManager mgr, long transactionID) {
return PREPARED;
}
public int prepareAndCommit(TransactionManager mgr, long transactionID) {
int result = prepare(mgr, transactionID);
if (result == PREPARED) {
commit(mgr, transactionID);
result = COMMITTED;
}
return result;
}
}
|
Non.java
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.transaction.server.TransactionParticipant;
public class Non extends UnicastRemoteObject implements TransactionParticipant {
public Non() throws RemoteException {
super();
}
public synchronized void abort(TransactionManager mgr, long transactionID) {
}
public synchronized void commit(TransactionManager mgr, long transactionID) {
}
public int prepare(TransactionManager mgr, long transactionID) {
return ABORTED;
}
public int prepareAndCommit(TransactionManager mgr, long transactionID) {
int result = prepare(mgr, transactionID);
if (result == PREPARED) {
commit(mgr, transactionID);
result = COMMITTED;
}
return result;
}
}
|
Vote.java
import java.io.IOException;
import java.rmi.RMISecurityManager;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
public class Vote {
public static void main(String[] args) throws Exception {
String host ="jini://localhost";
System.setSecurityManager(new RMISecurityManager());
if (args.length != 0)
host = "jini://" + args[0];
new Vote(host);
}
public Vote(String host) throws IOException {
try {
LookupLocator lookup = new LookupLocator(host);
ServiceRegistrar registrar = lookup.getRegistrar();
new Urne(registrar);
} catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
}
}
|
Urne.java
import java.rmi.RemoteException;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.transaction.server.TransactionManager.Created;
public class Urne {
TransactionManager manager;
Created leasedTransaction;
public Urne(ServiceRegistrar registrar) throws RemoteException {
Class[] txService = { TransactionManager.class };
ServiceTemplate template = new ServiceTemplate(null, txService, null);
manager = (TransactionManager) registrar.lookup(template);
if (manager == null) {
System.out.println("Impossible de trouver TransactionManager");
System.exit(0);
}
try {
leasedTransaction = manager.create(1000 * 60 * 2);
System.out.println("Dépouillement 1: 4 voix pour le oui.");
manager.join(leasedTransaction.id, new Oui(), 0);
manager.join(leasedTransaction.id, new Oui(), 0);
manager.join(leasedTransaction.id, new Oui(), 0);
manager.join(leasedTransaction.id, new Oui(), 0);
manager.commit(leasedTransaction.id);
System.out.println("Vote à l'unanimité.");
System.out.println("Dépouillement 2: 3 voix pour oui et une pour non");
manager.join(leasedTransaction.id, new Oui(), 0);
manager.join(leasedTransaction.id, new Oui(), 0);
manager.join(leasedTransaction.id, new Non(), 0);
manager.join(leasedTransaction.id, new Oui(), 0);
manager.commit(leasedTransaction.id);
} catch (Exception e) {
System.out.println("Vote divisé.");
}
}
}
|
Pour l'exécution:
- lancer le service lookupService.
- lancer le service TransactionManager qui est un service Jini gérant les transactions, en utilisant le jar « start » fournit par Jin
#java -jar -Djava.security.policy=priv.policy C:\jini2_1\lib\start.jar start-transient-mahalo.config
Avec priv.policy fichier de privilèges et start-transient-mahalo.config fichier de configuration du Transactionmanager qui nécessite à son tour le fichier transient-mahalo.config
- mettre en ligne les classes du programme pour le chargement dynamique (voir rmi) .
- lancer le client:
#java -Djava.rmi.server.codebase=http://localhost/export -Djava.security.policy=priv.policy Vote
Télécharger l'archive complet de ce tutoriel (Jini.zip)
|