Telechargé par salah.nezzar.19

Chapitre2 RMI

publicité
Chapitre II Architecture Multi-composants réparties
1-Notion d’objets répartis
Une architecture logicielle à base d’objets répartis est constituée d’un ensemble d’objets conçus pour
travailler en collaboration.
Mais contrairement à une application objet traditionnelle, les objets résident :
• Sur différents ordinateurs connectés via un réseau.
• Dans différents processus sur la même machine.
Un objet réparti peut envoyer un message à un autre objet réparti sur une machine distante ou
processus pour exécuter une tâche bien précise et ensuite en exploiter le résultat.
• Mais la puissance des objets répartis ne réside pas dans le fait qu’ils soient éparpillés sur un réseau :
leur intérêt principal provient du fait qu’un objet distant reste accessible de la même manière qu’un objet
local du système par invocation de méthodes.
2- La présentation et l'architecture de RMI
RMI (Remote Method Invocation : Le mécanisme d'invocation de méthode à distance) est une
technologie développée et fournie par Sun à partir du JDK 1.1 pour permettre de mettre en œuvre
facilement des objets distribués.
RMI est un ensemble de classes permettant de manipuler des objets sur des machines distantes (objets
distants) de manière similaire aux objets sur la machine locale (objet locaux).
On dit généralement que RMI est une solution "tout Java", contrairement à la norme Corba de l'OMG
(Object Management Group) permettant de manipuler des objets à distance avec n'importe quel langage.
Corba est toutefois beaucoup plus compliqué à mettre en œuvre, c'est la raison pour laquelle de
nombreux développeurs se tournent généralement vers RMI.
Le but de RMI est de permettre l'appel, l'exécution et le renvoi du résultat d'une méthode exécutée dans
une machine virtuelle différente de celle de l'objet l'appelant. Cette machine virtuelle peut être sur une
machine différente pourvu qu'elle soit accessible par le réseau.
La machine sur laquelle s'exécute la méthode distante est appelée serveur.
L'appel coté client d'une telle méthode est un peu plus compliqué que l'appel d'une méthode d'un objet
local mais il reste simple. Il consiste à obtenir une référence sur l'objet distant puis à simplement appeler
la méthode à partir de cette référence.
La technologie RMI se charge de rendre transparente la localisation de l'objet distant, son appel et le
renvoi du résultat.
En fait, elle utilise deux classes particulières, le stub et le skeleton, qui doivent être générées avec l'outil
rmic fourni avec le JDK.
Le stub est une classe qui se situe côté client et le skeleton est son homologue coté serveur. Ces deux
classes se chargent d'assurer tous les mécanismes d'appel, de communication, d'exécution, de renvoi et
de réception du résultat.
Un stub est un objet coté client qui gère l’encodage et le désencodage des données lors d’un appel à un
objet distant RMI.
Le client croit invoquer l’objet distant mais il invoque en fait son stub qui implémente la même interface
et gère toute la problématique réseau. Le stub est lié à un skeleton.
Un skeleton est un objet coté serveur qui gère l’encodage et le désencodage des données lors de la
réception d’un appel à un objet RMI.
3- Structure des couches RMI
Les connexions et les transferts de données dans RMI sont effectués par Java sur TCP/IP grâce à
un protocole propriétaire (JRMP, Java Remote Method Protocol) sur le port 1099.
A partir de Java 2 version 1.3, les communications entre client et serveur s'effectuent grâce au
protocole RMI-IIOP (Internet Inter-Orb Protocol), un protocole normalisé par l'OMG (Object
Management Group) et utilisé dans l'architecture CORBA.
La transmission de données se fait à travers un système de couches, basées sur le modèle OSI afin
de garantir une interopérabilité entre les programmes et les versions de Java.

Le stub (traduisez souche) et le skeleton (traduisez squelette), respectivement sur le client et
le serveur, assurent la conversion des communications avec l'objet distant.

La couche de référence (RRL, remote Reference Layer) est chargé du système de localisation
afin de fournir un moyen aux objets d'obtenir une référence à l'objet distant. Elle est assurée
par le package java.rmi.Naming. On l'appelle généralement registre RMI car elle référence les
objets.

La couche de transport permet d'écouter les appels entrants ainsi que d'établir
les connexions et le transport des données sur le réseau par l'intermédiaire du protocole TCP. Les
packages java.net.Socket et
java.net.SocketServer assurent
implicitement
cette
fonction.
Ainsi, une application client-serveur basé sur RMI met ainsi en œuvre trois composantes :

une application cliente implémentant le stub

une application serveur implémentant le skeleton (squelette)

une application médiatrice (le registre RMI) servie par un processus tiers (rmiregistry)
4- Architecture générale d’un système RMI
La figure suivante illustre l’architecture générale d’un système RMI. Dans cette architecture, un serveur
RMI désire rendre accessible un certain nombre de ses méthodes à des clients RMI. Le client et le
serveur RMI sont tous les deux des objets java qui peuvent être exécutés sur des machines différentes.
Une troisième composante agît comme un “service d’annuaire” entre le client et le serveur : la RMI
registry.
Elle permet au client de trouver un serveur distant qui pourra lui rendre certains services. La notion de
service est fondamentale en RMI et plus généralement en informatique distribuée et rejoint la notion de
contrat en programmation orientée objet (POO).
Minimalement, un système RMI repose sur trois phases :
1. opération de bind/rebind : durant cette phase, le serveur RMI demande à la RMI registry de créer
une nouvelle entrée dans son “annuaire” afin de rendre ces méthodes visibles aux clients RMI. La
nouvelle entrée de l’annuaire associera un nom au serveur RMI.
2. opération de lookup : durant cette phase, le client RMI demande à la RMI registry de lui donner le
serveur RMI associé à un certain nom dans son annuaire. Il est donc nécessaire que le client connaisse le
nom sous lequel le serveur a été inscrit dans l’annuaire de la registry.
3. invocation de méthodes distantes : maintenant le client peut invoquer les méthodes du serveur.
Les appels de méthodes distantes sont presque aussi simples que les appels de méthodes locales.
5- Etapes d’un appel de méthode distante
6- Les différentes étapes pour créer un objet distant et l'appeler avec RMI
Le développement coté serveur se compose de :



La définition d'une interface qui contient les méthodes qui peuvent être appelées à distance
L'écriture d'une classe qui implémente cette interface
L'écriture d'une classe qui instanciera l'objet et l'enregistrera en lui affectant un nom dans le
registre de noms RMI (RMI Registry)
Le développement côté client se compose de :


L'obtention d'une référence sur l'objet distant à partir de son nom
L'appel à la méthode à partir de cette référence
Enfin, il faut générer les classes stub et skeleton en exécutant le programme rmic avec le fichier source
de l'objet distant.
6-1 Le développement coté serveur
Côté serveur, l'objet distant est décrit par une interface. Une instance de l'objet doit être créée et
enregistrée dans le registre RMI.
6-1-1 La définition d'une interface qui contient les méthodes de l'objet distant
L'interface à définir doit hériter de l'interface java.rmi.Remote.
L'interface doit contenir toutes les méthodes qui seront susceptibles d'être appelées à distance.
Il doit être déclaré public pour que les clients puissent charger des objets distants qui implémentent
l'interface distante.
La communication entre le client et le serveur lors de l'invocation de la méthode distante peut échouer
pour diverses raisons telles qu'un crash du serveur, une rupture de la liaison, etc ...
Ainsi chaque méthode appelée à distance doit déclarer qu'elle est en mesure de lever l'exception
java.rmi.RemoteException.
Exemple ( code Java 1.1 ) :
package exemple1.rmi;
import java.rmi.*;
// l’interface distant
public interface Information extends Remote {
public String getInformation() throws RemoteException;
}
6-1-2 L'écriture d'une classe qui implémente cette interface
Cette classe correspond à l'objet distant. Elle doit donc implémenter l'interface définie et contenir le code
nécessaire.
Cette classe doit obligatoirement hériter de la classe UnicastRemoteObject qui contient les différents
traitements élémentaires pour un objet distant dont l'appel par le stub du client est unique. Le stub ne
peut obtenir qu'une seule référence sur un objet distant héritant de la classe UnicastRemoteObject. On
peut supposer qu'une future version de RMI sera capable de faire du MultiCast, permettant à RMI de
choisir parmi plusieurs objets distants identiques la référence à fournir au client.
Comme indiqué dans l'interface, toutes les méthodes distantes, mais aussi le constructeur de la classe,
doivent indiquer qu'elles peuvent lever l'exception RemoteException. Ainsi, même si le constructeur ne
contient pas de code il doit être redéfini pour inhiber la génération du constructeur par défaut qui ne lève
pas cette exception.
Exemple ( code Java 1.1 ) :
package exemple1.rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
//class implémentant la méthode getInformation
public class InformationImpl extends UnicastRemoteObject implements Information
{
private static final long serialVersionUID = 2674880711467464646L;
// constructeur
protected InformationImpl() throws RemoteException {
super();
}
// méthode réalisant getInformation
public String getInformation() throws RemoteException {
System.out.println("Invocation de la méthode getInformation()");
return "bonjour";
}
}
6-1-3 L'écriture d'une classe pour instancier l'objet et l'enregistrer dans le registre
Ces opérations peuvent être effectuées dans la méthode main d'une classe dédiée ou dans la méthode
main de la classe de l'objet distant. L'intérêt d'une classe dédiée et qu'elle permet de regrouper toutes
ces opérations pour un ensemble d'objets distants.
La marche à suivre contient trois étapes :



la mise en place d'un security manager dédié qui est facultative
l'instanciation d'un objet de la classe distante
l'enregistrement de la classe dans le registre de noms RMI
6-1-3-1 La mise en place d'un security manager
Cette opération n'est pas obligatoire mais elle est recommandée en particulier si le serveur doit charger
des classes récupérées sur des machines distantes. Sans security manager, il faut obligatoirement
mettre à la disposition du serveur toutes les classes dont il aura besoin (Elles doivent être dans le
CLASSPATH du serveur). Avec un security manager, le serveur peut charger dynamiquement certaines
classes.
Cependant, le chargement dynamique de ces classes peut poser des problèmes de sécurité car le serveur
va exécuter du code d'une autre machine. Cet aspect peut conduire à ne pas utiliser de security
manager.
Exemple ( code Java 1.1 ) :
public static void main(String[] args) {
try {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
} catch (Exception e) {
e.printStrackTrace();
}
}
6-1-3-2 L'instanciation d'un objet de la classe distante
Cette opération est très simple puisqu'elle consiste simplement en la création d'un objet de la classe de
l'objet distant
Exemple ( code Java 1.1 ) :
public static void main(String[] args) {
try {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
InformationImpl informationImpl = new InformationImpl();
} catch (Exception e) {
e.printStrackTrace();
}
}
6-1-2-3 L'enregistrement dans le registre de noms RMI
La dernière opération consiste à enregistrer l'objet créé dans le registre de noms en lui affectant un nom.
Ce nom est fourni au registre sous forme d'une URL constituée du préfix rmi://, du nom du serveur
(hostname) et du nom associé à l'objet précédé d'un slash.
Le nom du serveur peut être fourni « en dur » sous forme d'une constante chaîne de caractères ou peut
être dynamiquement obtenu en utilisant la classe InetAddress pour une utilisation en locale.
C'est ce nom qui sera utilisé dans une URL par le client pour obtenir une référence sur l'objet distant.
L'enregistrement se fait en utilisant la méthode rebind de la classe Naming. Elle attend en paramètre
l'URL du nom de l'objet et l'objet lui-même.
Exemple ( code Java 1.1 ) :
public static void main(String[] args) {
try {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
InformationImpl informationImpl = new InformationImpl();
String url = "rmi://" + InetAddress.getLocalHost().getHostAddress() + "/TestRMI";
System.out.println("Enregistrement de l'objet avec l'url : " + url);
Naming.rebind(url, informationImpl);
System.out.println("Serveur lancé");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
6-1-2-4 Le lancement dynamique du registre de noms RMI
Sur le serveur, le registre de noms RMI doit s'exécuter avant de pouvoir enregistrer un objet ou obtenir
une référence.
Ce registre peut être lancé en tant qu'application fournie dans le JDK (rmiregistry) comme indiqué dans
un chapitre suivant ou être lancé dynamiquement dans la classe qui enregistre l'objet. Ce lancement ne
doit avoir lieu qu'une seule et unique fois. Il peut être intéressant d'utiliser le code ci-dessous si l'on crée
une classe dédiée à l'enregistrement des objets distants.
Le
code
pour
exécuter
le
registre
est
la
méthode
createRegistry()
de
java.rmi.registry.LocateRegistry. Cette méthode attend en paramètre un numéro de port.
la
classe
Exemple ( code Java 1.1 ) :
package exemple1.rmi;
import
import
import
import
import
import
import
java.net.InetAddress;
java.net.MalformedURLException;
java.net.UnknownHostException;
java.rmi.Naming;
java.rmi.RMISecurityManager;
java.rmi.RemoteException;
java.rmi.registry.LocateRegistry;
public class LanceServeur {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
System.out.println("Mise en place du Security Manager ...");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
InformationImpl informationImpl = new InformationImpl();
String url = "rmi://" + InetAddress.getLocalHost().getHostAddress() + "/TestRMI";
System.out.println("Enregistrement de l'objet avec l'url : " + url);
Naming.rebind(url, informationImpl);
System.out.println("Serveur lancé");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
6-2 Le développement coté client
L'appel d'une méthode distante peut se faire dans une application ou dans une applet.
6-2-1 La mise en place d'un security manager
Comme pour le coté serveur, cette opération est facultative.
Le choix de la mise en place d'un sécurity manager côté client suit des règles identiques à celles
appliquées côté serveur. Sans son utilisation, il est nécessaire de mettre dans le CLASSPATH du client
toutes les classes nécessaires dont la classe stub.
Exemple ( code Java 1.1 ) :
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}
6-2-2 L'obtention d'une référence sur l'objet distant à partir de son nom
Pour obtenir une référence sur l'objet distant à partir de son nom, il faut utiliser la méthode statique
lookup() de la classe Naming.
Cette méthode attend en paramètre une URL indiquant le nom qui référence l'objet distant. Cette URL
est composée de plusieurs éléments : le préfix rmi://, le nom du serveur (hostname) et le nom de l'objet
tel qu'il a été enregistré dans le registre précédé d'un slash.
Il est préférable de prévoir le nom du serveur sous forme de paramètres de l'application ou de l'applet
pour plus de souplesse.
La méthode lookup() va rechercher l'objet dans le registre du serveur et retourner un objet stub. L'objet
retourné est de la classe Remote (cette classe est la classe mère de tous les objets distants).
Si le nom fourni dans l'URL n'est pas référencé dans le registre, la méthode lève l'exception
NotBoundException.
Exemple ( code Java 1.1 ) :
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
Remote r = Naming.lookup("rmi://10.0.0.13/TestRMI");
} catch (Exception e) {
e.printStrackTrace();
}
}
6-2-3 L'appel de la méthode à partir de la référence sur l'objet distant
L'objet retourné étant de type Remote, il faut réaliser un cast vers l'interface qui définit les méthodes de
l'objet distant. Pour plus de sécurité, on vérifie que l'objet retourné est bien une instance de cette
interface.
Un fois le cast réalisé, il suffit simplement d'appeler la méthode.
Exemple ( code Java 1.1 ) :
package exemple1.rmi;
import
import
import
import
import
import
java.net.MalformedURLException;
java.rmi.Naming;
java.rmi.NotBoundException;
java.rmi.RMISecurityManager;
java.rmi.Remote;
java.rmi.RemoteException;
public class LanceClient {
public static void main(String[] args) {
System.out.println("Lancement du client");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
Remote r = Naming.lookup("rmi://10.0.0.13/TestRMI");
System.out.println(r);
if (r instanceof Information) {
String s = ((Information) r).getInformation();
System.out.println("chaine renvoyee = " + s);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
System.out.println("Fin du client");
}
}
6-2-4 L'appel d'une méthode distante dans une applet
L'appel d'une méthode distante est le même dans une application et dans une applet.
Seule la mise en place d'un security manager dédié dans les applets est inutile car elles utilisent déjà un
sécurity manager (AppletSecurityManager) qui autorise le chargement de classes distantes.
Exemple ( code Java 1.1 ) :
package exemple1.rmi;
import java.applet.*;
import java.awt.*;
import java.rmi.*;
public class AppletTestRMI extends Applet {
private String s;
public void init() {
try {
Remote r = Naming.lookup("rmi://10.0.0.13/TestRMI");
if (r instanceof Information) {
s = ((Information) r).getInformation();
}
} catch (Exception e) {
e.printStrackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
g.drawString("chaine retournée = "+s,20,20);
}
}
6-3 La génération de la classe stub
Pour générer la classe stub, il suffit d'utiliser l'outil rmic fourni avec le JDK en lui donnant en paramètre
le nom pleinement qualifié de la classe.
Attention la classe doit avoir été compilée : rmic a besoin du fichier .class.
Exemple ( code Java 1.1 ) :
rmic exemple1.rmi.InformationImpl
rmic va générer et compiler la classe stub sous le nom InformationImpl_Stub.class. Cette classe sera
utilisée par la partie cliente pour invoquer l'objet distant correspondant.
6-4 La mise en oeuvre des objets RMI
La mise en oeuvre et l'utilisation d'objets distants avec RMI nécessite plusieurs étapes :
1.
2.
3.
Démarrer le registre RMI sur le serveur soit en utilisant le programme rmiregistry livré avec le
JDK soit en exécutant une classe qui effectue le lancement.
Exécuter la classe qui instancie l'objet distant et l'enregistre dans le serveur de noms RMI
Lancer l'application ou l'applet pour tester.
6-4-1 Le lancement du registre RMI
La commande rmiregistry est fournie avec le JDK. Il faut la lancer en tâche de fond :
Sous Unix : rmiregistry&
Sous Windows : start rmiregistry
Ce registre permet de faire correspondre un objet à un nom et inversement. C'est lui qui est sollicité lors
d'un appel aux méthodes Naming.bind() et Naming.lookup()
6-4-2 L'instanciation et l'enregistrement de l'objet distant
Il faut exécuter la classe qui va instancier l'objet distant et l'enregistrer sous son nom dans le registre
précédemment lancé.
Pour ne pas avoir de problème, il faut s'assurer que toutes les classes utiles (la classe de l'objet distant,
l'interface qui définit les méthodes) sont présentes dans un répertoire défini dans le classpath.
Si un gestionnaire de sécurité est mis en place, il faut définir un fichier qui va contenir la politique de
sécurité qu'il doit mettre en oeuvre.
Exemple ( code Java 1.1 ) : le fichier ma_policy_serveur
grant{
permission java.net.SocketPermission "localhost:1099", "connect, resolve";
permission java.net.SocketPermission "*:1024-", "connect, resolve";
permission java.net.SocketPermission "*:1024-", "accept, resolve";
};
Les permissions définies concernent les permissions de connexions par socket au serveur.
Lors du lancement du serveur, l'option java.security.policy permet de préciser le fichier qui sera utilisé
par le gestionnaire de sécurité.
Exemple ( code Java 1.1 ) : le fichier ma_policy_serveur
C:\Users\Jean Michel\workspace\TestRmiServer>java -cp bin -Djava.security.policy
=ma_policy_serveur exemple1.rmi.LanceServeur
Mise en place du Security Manager ...
Enregistrement de l'objet avec l'url : rmi://10.0.0.13/TestRMI
Serveur lancé
6-5 Le lancement de l'application cliente
L'archive de la partie cliente doit contenir le client, l'interface de l'objet distant et le stub qui a été généré
par rmic.
Exemple :
C:\temp>jar -tf TestRMIClient.jar
META-INF/MANIFEST.MF
com/jmdoudoux/test/rmi/Information.class
com/jmdoudoux/test/rmi/InformationImpl_Stub.class
com/jmdoudoux/test/rmi/LanceClient.class
Le client qui invoque l'objet distant est lancé de manière classique.
Exemple :
C:\temp>java -jar TestRMIClient.jar
Lancement du client
InformationImpl_Stub[UnicastRef [liveRef: [endpoint:[10.0.0.13:62802](remote),ob
jID:[7b7739e4:135b4a87a5e:-7fff, -3323459310870193038]]]]
chaine renvoyee = bonjour
Fin du client
Si le serveur n'est pas démarré, une exception est levée
Exemple :
C:\temp>java -jar TestRMIClient.jar
Lancement du client
java.rmi.ConnectException: Connection refused to host: 10.0.0.13; nested excepti
on is:
java.net.ConnectException: Connection timed out: connect
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
at sun.rmi.server.UnicastRef.newCall(Unknown Source)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at java.rmi.Naming.lookup(Unknown Source)
at exemple1.rmi.LanceClient.main(LanceClient.java:17)
Caused by: java.net.ConnectException: Connection timed out: connect
at java.net.TwoStacksPlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(Unknown S
ource)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(Unknown S
ource)
... 7 more
Fin du client
La partie client peut être lancée avec un gestionnaire et une politique de sécurité associée.
Exemple ( code Java 1.1 ) :
C:\temp>java -jar -Djava.security.policy=ma_policy_client TestRMIClient.jar
Lancement du client
InformationImpl_Stub[UnicastRef [liveRef: [endpoint:[10.0.0.13:62802](remote),ob
jID:[7b7739e4:135b4a87a5e:-7fff, -3323459310870193038]]]]
chaine renvoyee = bonjour
Fin du client
Si le gestionnaire est activé sans politique de sécurité associée alors la connexion au serveur est
impossible.
Exemple ( code Java 1.1 ) :
C:\temp>java -jar -Djava.security.manager TestRMIClient.jar
Lancement du client
Exception in thread "main" java.security.AccessControlException: access denied (
"java.net.SocketPermission" "10.0.0.13:1099" "connect,resolve")
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkConnect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(Unknown S
ource)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(Unknown S
ource)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
at sun.rmi.server.UnicastRef.newCall(Unknown Source)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at java.rmi.Naming.lookup(Unknown Source)
at exemple1.rmi.LanceClient.main(LanceClient.java:17)
Téléchargement