Esercizio 2.1: Gestione del Registro RMI attraverso la classe Naming
(updated).
L'esercizio consiste nel registrare un certo numero di oggetti remoti
(in questo caso 5) su un certo numero di registri RMI (in questo caso due)
che girano
ovviamente su una macchina server su porte (ovviamente) differenti. Dopodiche'
un programma ListRegistry si preoccupera' di stampare il contenuto dei due
registry.
1) Nella directory ~/javarmi create una directory registry
e ricopiate al suo interno l'interfaccia remota Hello.java simile
a quella vista nell'esercizio precedente
in cui cambiate il nome del package:
package registry;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
2) Nella stessa directory registry ricopiate una variante dell'implementazione
HelloImpl.java dell'esercizio precedente che si occupa di creare
5 oggetti remoti che vengono
registrati su due registri RMI differenti; il primo registro gira alla
porta 2345 ed il secondo alla porta 2333 (usate altre porte se lo preferite).
Nel primo registro vengono registrati 2 oggetti mentre nel secondo registro
vengono registrati i rimanenti 3 oggetti. Nota che gli oggetti vengono
esportati su porte prestabilite dall'utente, cioe', la 2222, 3333, 4444,
5555, 6666. Cio' e fatto specificando nel costruttore dell'oggetto UnicastRemoteObject
la porta sulla quale si vuole che venga esportato l'oggetto.
package registry;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject
implements Hello {
public HelloImpl(int port)
throws RemoteException {
super(port);
// l'oggetto viene esportato alla porta 'port'.
}
public String sayHello() {
return
"Hello World: I am your remote object!";
}
public static void main(String
args[]) {
//
Create and install a security manager
if
(System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try
{
Hello obj1 = new HelloImpl(2222); // oggetto obj1 e' esportato alla
porta 2222
// Bind this object instance to the name "HelloServer"
Naming.rebind("//:2345/HelloServer1", obj1);
System.out.println("Remote Object HelloServer1 bound in RMI registry at
port 2345");
Hello obj2 = new HelloImpl(3333); // oggetto obj2 e' esportato
alla porta 3333
// Bind this object instance to the name "HelloServer"
Naming.rebind("//:2345/HelloServer2", obj2);
System.out.println("Remote Object HelloServer2 bound in RMI registry at
port 2345");
Hello obj3 = new HelloImpl(4444); // oggetto obj3 e' esportato
alla porta 4444
// Bind this object instance to the name "HelloServer"
Naming.rebind("//:2333/HelloServer3", obj3);
System.out.println("Remote Object HelloServer3 bound in RMI registry at
port 2333");
Hello obj4 = new HelloImpl(5555); // oggetto obj4 e' esportato
alla porta 5555
// Bind this object instance to the name "HelloServer"
Naming.rebind("//:2333/HelloServer4", obj4);
System.out.println("Remote Object HelloServer4 bound in RMI registry at
port 2333");
Hello obj5 = new HelloImpl(6666);
// Bind this object instance to the name "HelloServer"
Naming.rebind("//:2333/HelloServer5", obj5); // oggetto obj5
e' esportato alla porta 6666
System.out.println("Remote Object HelloServer5 bound in RMI registry at
port 2333");
}
catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
3) Nella directory registry aggiungete il file policy utilizzato
nell'esercizio precedente.
4) Compilate i sorgenti nella directory registry col comando
javac Hello.java HelloImpl.java
5) Create le referenze remote (gli stub) che saranno necessari quando
andremo a registrare gli oggetti nei registri. Percio' andate nella directory
padre,
javarmi, ed eseguite il comando
rmic registry.HelloImpl.
6) A questo punto lanciate i due registry RMI. Prima pero' fate
unset CLASSPATH (in realta' in questo esercizio specifico non sarebbe
necessario
perche' non usiamo codebase). Digitiamo allora
i seguenti comandi:
unset CLASSPATH
rmiregistry 2345 &
rmiregistry 2333 &
7) A questo punto siete pronti a lanciare l'implementazione. Andate in
javarmi ed eseguite il seguente comando:
java -Djava.security.policy=registry/policy registry.HelloImpl
&. Se tutto va come desiderato sul
vostro schermo apparira' la seguente videata:
Remote Object HelloServer1 bound in RMI registry at port 2345
Remote Object HelloServer2 bound in RMI registry at port 2345
Remote Object HelloServer3 bound in RMI registry at port 2333
Remote Object HelloServer4 bound in RMI registry at port 2333
Remote Object HelloServer5 bound in RMI registry at port 2333
Che vi segnala che i primi due oggetti (HelloServer1 ed HelloServer2)
sono stati registrati nel registro alla porta 2345, mentre gli altri tre
nel registro alla porta 2333.
8) Adesso scriviamo un programma che usando la classe Naming legga
il contenuto di questi due registry. Nella stessa directory registry aggiungiamo
il
programma ListRegistry.java con il seguente
codice:
package registry;
import java.rmi.*;
import java.util.*;
// List the names and objects bound in RMI Registries.
// Invoke with zero or more registry URLs as command-line arguments,
// e.g. "rmi://localhost", "rmi://localhost:1099".
public class ListRegistry
{
public static void main(String[] args)
{
// Create and install a security manager
//System.setProperty("java.security.debug","access,failure");
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
for (int i = 0; i < args.length; i++)
{
try
{
String[] list = Naming.list(args[i]);
System.out.println("Contents of registry at "+args[i]+
" ("+list.length+" entries)");
for (int j = 0; j < list.length; j++)
{
Remote remote = Naming.lookup(list[j]);
// remember that the lookup method return a remote reference,
that is a stub.
System.out.println((j+1)+".\tname="+list[j]+"\n\tremote="+remote);
}
}
catch (java.net.MalformedURLException e)
{
System.err.println(e);
}
catch (NotBoundException e)
{
// name vanished between list and lookup - ignore
}
catch (RemoteException e)
{
System.err.println(e);
}
}
}
}
9) Osservando il programma capirete che esso ricevera' in ingresso un
certo numero di indirizzi rmi dove trovare dei registri. Tali URL sono memorizzati
in args che e' un
array. Dopodiche', per ciascun elemento di args (cioe'
per ciascun registro) esegue una operazione Naming.list per ottenere un
array dei nomi di oggetti che sono
registrati nel registro, tale array viene memorizzato
in list. Per ciascuno di questi oggetti viene fatta una lookup. L'operazione
di lookup ritorna lo stub, la referenza remota,
all'oggetto in questione. Tale stub viene allora stampato
insieme al nome dell'oggetto. Il risultato finale sara' una videata in
cui per ogni registro viene riportato il
suo contenuto: cioe' la coppia (nome oggetto, referenza
remota).
10) Compiate ListRegistry.java andando in registry e digitando
javac Listregistry.java.
11) A questo punto potete lanciare il bytecode di ListRegistry andando
in javarmi digitando:
java -Djava.security.policy=registry/policy
registry.ListRegistry rmi://localhost:2345 rmi://localhost:2333
(se non dovesse funzionare provate a sostiutire
localhost col nome della vostra macchina. A volte.....).
12) Se siete in giornata fortunata vi dovrebbe spuntare la seguente videata:
Contents of registry at rmi://localhost:2345 (2 entries)
1. name=rmi://localhost:2345/HelloServer1
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:2222](remote),objID:[c3c749:f5279a5fb0:-8000,
0]]]]
2. name=rmi://localhost:2345/HelloServer2
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:3333](remote),objID:[c3c749:f5279a5fb0:-8000,
1]]]]
Contents of registry at rmi://localhost:2333 (3 entries)
1. name=rmi://localhost:2333/HelloServer3
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:4444](remote),objID:[c3c749:f5279a5fb0:-8000,
2]]]]
2. name=rmi://localhost:2333/HelloServer4
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:5555](remote),objID:[c3c749:f5279a5fb0:-8000,
3]]]]
3. name=rmi://localhost:2333/HelloServer5
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:6666](remote),objID:[c3c749:f5279a5fb0:-8000,
4]]]]
Cercate di capire esattamente come tale schermata sia venuta fuori
e cosa vogliano dire i vari campi notate che la referenza remota
conserva l'informazione circa dove si trova il server remoto e su quale porta
e' esportato, l'ultimo campo e' un identificatore usato dal
sistema RMI.
Esercizio 2.2. Gestione del registro RMI attraverso la classe Registry.
1) In questo esercizio invece di usare la classe Naming userete
la classe Registry per creare una videata simile a quella vista
sopra.
Sostituiremo ListRegistry.java con un altro sorgente java che fa uso
di Registry. Tale sorgente lo chiameremo (con molta fantasia)
ListRegistry2.java. Il funzionamento del programma e' simile a
ListRegistry. Il contenuto del sorgente e' il seguente:
package registry;
import java.rmi.*;
import java.rmi.registry.*;
import java.util.*;
// Same as ListRegistry but uses a Registry from LocateRegistry instead
of Naming.
// Invoke with hostname [port], e.g. "localhost", "localhost 1099".
public class ListRegistry2
{
public static void main(String[] args)
{
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
for (int i = 0; i < args.length; i++)
{
try
{
String host = args[i];
int port = Registry.REGISTRY_PORT;
if (i+1 < args.length)
{
try
{
port = Integer.parseInt(args[i+1]);
i++;
}
catch (NumberFormatException e)
{
// next arg isn't a port number
}
}
Registry registry = LocateRegistry.getRegistry(host,port);
// if the try construct failed we have in the variable
port the standard port number 1099 from Registry.REGISTRY_PORT
System.out.println("Contents of registry at "+host+":"+port);
String[] list = registry.list();
for (int j = 0; j < list.length; j++)
{
Remote remote = registry.lookup(list[j]);
// remember that the lookup method return a remote
reference, that is a stub.
System.out.println((j+1)+".\tname="+list[j]+"\n\tremote="+remote);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
2)Notate come la variabile port assuma inizialmente ad ogni iterazione
il valore Registry.REGISTRY_PORT che e' il valore 1099 di default.
Tale valore servira' quando il programma viene invocato con un nome di
host ma fornire la porta, che verra' allora assunta essere quella di default.
Gli argomenti forniti al programma saranno della forma localhost 2345
localhost localhost 2333. Cioe' un'aternarsi di stringhe che il programma
deve riconoscere
se si riferiscono ad host oppure se sono numeri di porte. Se il numero
di porta e' omesso viene utilizzata quella standard.
3) Compiliamo questo nuovo sorgente digitando javac ListRegistry2.java.
4) Per rendere le cose piu interessanti lanciamo il comando rmiregistry
&, che creera' un registro RMI vuoto alla porta 1099.
5) A questo punto ritorniamo nella directory javarmi e lanciamo il nuovo
codice con
java -Djava.security.policy=registry/policy registry.ListRegistry2 localhost
2345 localhost localhost 2333
Notate che in questo modo, il programma chiede informazioni su ben tre
registri. Il primo alla porta 2345, il secondo alla porta di default (visto
che il numero
di porta e' omesso) e il terzo alla porta 2333.
Se continuate ad essere in giornata fortunata dovreste ottenere la seguente
schermata.
Contents of registry at localhost:2345
1. name=HelloServer1
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:2222](remote),objID:[c3c749:f527c9390c:-8000,
0]]]]
2. name=HelloServer2
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:3333](remote),objID:[c3c749:f527c9390c:-8000,
1]]]]
Contents of registry at localhost:1099
Contents of registry at localhost:2333
1. name=HelloServer3
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:4444](remote),objID:[c3c749:f527c9390c:-8000,
2]]]]
2. name=HelloServer4
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:5555](remote),objID:[c3c749:f527c9390c:-8000,
3]]]]
3. name=HelloServer5
remote=registry.HelloImpl_Stub[RemoteStub
[ref: [endpoint:[127.0.0.1:6666](remote),objID:[c3c749:f527c9390c:-8000,
4]]]]
Anche qui studiate attentamente il codice per capire esattamente come
venga creata tale schermata.
Notate che la referenza remota di ciascun oggetto sostanzialemente contiene
informazioni su: (i) l'host dove l'oggetto remoto e' in esecuzione;
(ii) la porta in cui l'oggetto (e non il registro) e' in ascolto; (iii)
l'identificatore di oggetto interno al sistema RMI.
Esercizio 2.3.
Adesso potreste provare a rifare l'esercizio 2.1 in maniera distribuita.
Cioe', ad esempio, potreste riunirvi in gruppi di tre persone. Due
fanno i server e lanciano i loro
registri in cui registrano degli oggetti. Il terzo di voi fa il client
e cerchera' di eseguire la ListRegistry per ottenere il contenuto dei registri
sulle altre due macchine.
Adesso i parametri che si passeranno non saranno
piu' localhost ma il nome della macchina in cui stanno girando i registry.
Notate che adesso non siete piu' in locale e gli stub non sono piu' disponibili
localmente
ma devono essere caricati a tempo di esecuzioni come visto nell'esercizio
precedente. Usando il protocollo http:// oppure file://.
Da questo esercizio cosa imparate sul metodo Naming.list?
Esercizio 2.4
Ricordate che il metodo Naming.bind (o Naming.rebind) puo' essere
eseguito correttamente solo se il registro e l'applicazione che fa il binding
girano sullo stesso host (questo per motivi di sicurezza). E' pero' possibile
che la'pplicazine esegua il binding di un oggetto remoto che e' stato esportato
da un altro
server. Di tale oggetto remoto l'applicazione (che esegue il binding) e'
ovviamente in possesso solo di una referenza remota.
Scrivere allora una variante del server base HelloImpl che si occupa
di registrare nel registro RMI un oggetto remoto esportato da un'altro
server.