Sistema di naming JNDI.
- Nel capitolo 6 abbiamo visto come il registro RMI fornisca un meccanismo
di naming per registrare server RMI che devono essere usati inizialmente
(bootstrap) dai cients.
- Il registro RMI considera gli indirizzi (es. rmi://profs.sci.univr.it:2035/DataServer)
come indirizzi piatti, cioe' privi di gerarchia. Inoltre abbiamo la restrizione
dovuta da vincoli di sicurezza che l'applicazione che fa la 'bind' e il
registro devono girare sullo stesso host. Vediamo adesso meccanismo di Naming
piu' generali.
- JNDI (Java Naming and Directory INterface) fornisce
funzionalita' di Naming e directory per Java. In prima approssimazione,
JNDI fornisce un'interfaccia unica che consente di supportare
differenti servizi di Naming, quali: (1) Registri RMI;
(2) Il servizio di Naming COS di CORBA; (3) protocollo LDAP.
- L'API di JNDI e' sempre il medesimo indipendentemente dal servizio
di naming che viene supportato. L'unica differenza notata da un client JNDI
e' che (1) si dovra specificare quale dei 3 sopra menzioonai servizi di
naming si vuole implementare; (2) la possibilita' che una certa operazione
non sia supportata dal servizio di Naming voluto.
- JNDI, consente le stesse operazioni sui registri RMI, quali bind,
unbind, rebind, lookup, e list.
- Tali operazioni possono essere effettuate su JNDI contexts,
che sono una sorta di name space. Spesso, context possono avere sub-context,
implementando cosi una gerarchia simile a quella delle sottodirectory in un
file-system.
- JNDI consente di avere anche directory contexts per contenere un
insieme di attributi come ad esempio gil attributi di un impiegato come
nome, location, e-mail, ect.
- In JNDI i servizi di naming vengono acceduti attraverso plugins
chiamati providers. I servizi di naming che vengono acceduto tramite un
provider forniscono vantaggi quali la persistenza e la distribuzione su
una rete di calcolatori.
- La cosa importante da capire e' che il provider JNDI non e' il
vero servizio di naming ma e' solo un'interfaccia Java al servizio. Cioe'
il vero servizio di naming e' esterno al server. Cosi' se ad esempio decidiamo
di supportare un servizio di registro RMI, il registro dovra' comunque essere
lanciato separatamente.
- Proprieta' del provider. Il provider che viene acceduto da
JNDI e' controllato attraverso la proprieta' java.naming.factory.initial.
Con la proprieta' java.naming.provider.url viene fissata l'URL del
servizio di naming (ad esempio del registro RMI).
- Uno si potrebbe chiedere perche' complicarsi la vita usando JNDI
invece di usare direttamente un registro RMI. La ragione e' che JNDI permette
di supportare diverse forme di naming. Cosi' se in seguito si vuole supportare
un servizio di naming piu' sofisticate del registro RMI (quale ad esempio
LDAP) le modifice da fare sono estremamente limitate.
- Per configurare il provider del registro RMI bisogna settare
la proprieta ' java.naming.factory.initial con valore com.sun.jndi.rmi.registry.RegistryContextFactory
e la proprieta' java.naming.provider.url con con l'url del registro,
ad esempio rmi://profs.sci.univr.it:2567.
- Per configurare il provider per il servizio di naming COS in CORBA
bisogna settare la proprieta' java.naming.factory.initial con
com.sun.jndi.cosnaming.CNCtxFactory e la proprieta' java.naming.provider.url
con con valore apposito (che vedremo in seguito).
- Esempio. Una tipica operazione di lookup da parte di un cliente
su un registro RMI ha la seguente struttura:
import java.rmi.Naming;
MyRemote oggetto = (MyRemote)Naming.lookup("rmi://profs.sci.univr.it:2345/ServerRemoto);
- La mesedima operazione usando JNDI prende la seguente forma:
import javax.naming.*;
Properties pr = new Properties();
pr.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
pr.put(Context.PROVIDER_URL,"rmi://profs.sci.univr.it:4567");
InitialContext ctx = new
InitialContext(pr);
MyRemoto oggetto = (MyRemote)ctx.lookup(rmi://profs.sci.univr.it:4567/ServerRemoto);
- Nota che in questo esempio la context factory e l'url del provider
sono state settate da programma. Un'altra possibilita' sarebbe stata quella
di settarle da linea di comando dell'interprete java utilizzando le clausole
-Djava.naming.factory.initial.=......... e -Djava.naming.provider.url=.....
Esercizio 6: Registri RMI attraverso JNDI.
L'esercizio consiste nel programmare un client ed un server remoto. Il server
fornisce un oggetto remoto RemoteEcho come quello gia' vista in precedenza.
La differenza consiste nel fatto che l'oggetto viene registrato nel registro
RMI attraverso JNDI. E similmente il client accede all'oggetto utilizzando
l'API di JNDI. Comiciamo a descrivere
le operazione che deve fare il server.
1) Il server deve creare all'interno della directory javarmi una directory
jndi che fungera' da package. All'interno di tale package mettteremo
i seguenti sorgenti:
a) L'interfaccia remota RemoteEcho.java constituita da:
package jndi;
import java.rmi.*;
/** Third Echo example; remote method invocation */
public interface RemoteEcho extends Remote
{
public Object echo(Object object) throws
RemoteException;
}
b) L'implentazione del server remoto RemoteEchoServer.java costituito
da:
package jndi;
import java.rmi.*;
import java.rmi.server.*;
/** Third Echo example; remote method invocation */
public class RemoteEchoServer extends UnicastRemoteObject implements RemoteEcho
{
public RemoteEchoServer() throws RemoteException {}
public Object echo(Object object)
{ return object; }
}
c) Un programma BindJNDI.java che si occupa di creare un'istanza
dell'oggetto remoto e di legare tale istanza nel registro RMI; infine
si visulaizza il contenuto del registro.
package jndi;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import javax.naming.*;
// Fa il Bind di un server RMI via JNDI.
// USIAMO com.sun.jndi.cosnaming.CNCtxFactory ( per CORBA IIOP CosNaming),
// USIAMO com.sun.jndi.rmi.registry.RegistryContextFactory (per RMI Registry).
public class BindJNDI
{
public static void main(String[] args)
{
try
{
Properties props = new Properties();
System.out.println("Viene settata la proprieta' java.naming.factory.initial.");
props.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
// L'URL del registro la immettiamo da linea di comando
//props.put(Context.PROVIDER_URL,args[0]);
// System.out.println("La proprieta' java.naming.provider.url
la diamo da linea di comando.");
System.out.println("La proprieta' java.naming.provider.url
dove allocare il registro RMI e' settata col valore " + System.getProperty("java.naming.provider.url"));
Context cxt = new InitialContext(props);
System.out.println("Viene creata un'istanza dell'oggetto remoto.
Si ricordi che un oggetto UnicastRemoteObject viene esportato alla creazione.");
RemoteEcho h = new RemoteEchoServer();
System.out.println("Facciamo la binding dell'oggetto " + RemoteEcho.class.getName()
+" nel registro RMI.");
cxt.rebind(RemoteEcho.class.getName(),h);
System.out.println("Fatta la binding dell'oggetto " + RemoteEcho.class.getName()+
" nel registro RMI.");
System.out.println("Stampiamo il contenuto del registro
RMI.");
NamingEnumeration list = cxt.list(""); // list or listBindings
int j = 1;
while (list.hasMore())
{
NameClassPair item = (NameClassPair)list.next();// NameClassPair
or Binding
System.out.println(j+".\t"+item);
j++;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
2) Compiliamo i sorgenti in jndi col comando javac *.java
3) eseguiamo la compilazione rmic digitando da javarmi rmic -d ~/public_html/myclasses/
jndi.RemoteEchoServer. Qui sto supponendo che all'interno di public_html
voi abbiate una directory myclasses, al cui intermo verra' creato (automaticamente)
il package jndi con dentro gli stub.
4) Copiate la classe dell'interfaccia remota RemoteEcho.class nell'area
comune, cioe' in ~/public_html/myclasses/jndi/. Tale operazione e'
necessaria perche' il sistema
JNDI ha bisogno di caricare dinamicamente anche la classe
dell'interfaccia remota.
5) Andate nella home directory e digitate unset CLASSPATH
e poi lanciate il registro RMI con rmiregistry 4567 &. (ricordate
E' IMPORTANTE che il registro sia lanciato in una directory che non possa
vedere gli stub -- anche se chiusi dentro il package).
6) Ritornate nella directory javarmi e lanciate il programma di bind. Rimpiazzate
(i) $HOME con la vostra home, (ii) cambiate il codebase usando la vostra homepage;
(iii) cambiate il provider.url con le informazioni relative alla macchina
e la porta su cui e' stato lanciato il registro RMI. Esempio
java -classpath $HOME/javarmi/:$HOME/public_html/myclasses/
-Djava.rmi.server.codebase=http://arena..../myclasses/
-Djava.naming.provider.url=rmi://delta45:4567
jndi.BindJNDI
Notate come il mentre il valore java.naming.initial.factory sia fornito
da programma, il valore java.naming.provider.url viene fornito da linea di
comando.
Ovviamente si puo' scegliere una o l'altra opzione.
se tutto va bene vi apparira una schermata del tipo:
Viene settata la proprieta' java.naming.factory.initial.
La proprieta' java.naming.provider.url dove allocare il registro RMI e'
settata col valore rmi://profs.sci.univr.it:4567
Viene creata un'istanza dell'oggetto remoto. Si ricordi che un oggetto UnicastRemoteObject
viene esportato alla creazione.
Facciamo la binding dell'oggettojndi.RemoteEcho nel registro RMI.
Fatta la binding dell'oggetto remoto nel registro RMI.
Stampiamo il contenuto del registro RMI.
1. jndi.RemoteEcho: java.lang.Object
7) Passiamo al client. Anche lui deve creare una direcotry jndi sotto
javarmi. in tale directory oltre all'interfaccia remota RemoteEcho.java vista
prima ed al solito
file di policy mettiamo il codecie del client RemoteEchoClient.java:
package jndi;
import java.io.IOException;
import java.rmi.*;
import java.util.*;
import javax.naming.*;
import javax.rmi.*;
/**
* JNDI client of javarmi.mytests.jndi.RemoteEcho.
* Same as javarmi.mytests.quicktour.echo3.client.RemoteEchoClient,
* except that it uses JNDI with the RMI Registry plugin,
* instead of directly calling the Registry (by RemoteEchoFactory).
*/
public class RemoteEchoClient
{
public static void main(String[] args)
{
try
{
System.setSecurityManager(new SecurityManager());
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
// Qui l'URL la diamo da programma.
p.put(Context.PROVIDER_URL,"rmi://"+args[0]+":4567");
InitialContext context = new InitialContext(p);
RemoteEcho echo = (RemoteEcho)context.lookup(RemoteEcho.class.getName());
System.out.println(echo.echo("Io sono l'oggetto remoto con
naming JNDI!!!!"));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
8) Compililiamo il codice del client facendo javac *.java.
9) A questo punto siamo pronti a lanciare il client andando in javarmi.
Ricordiamoci che dobbiamo usare il file di policy poiche il client carichera'
del codice dinamicamente (lo stub). Chiaramente rimpiazzate profs.sci.univr.it
con la macchina dove gira il server.
java -Djava.security.policy=jndi/policy jndi.RemoteEchoClient profs.sci.univr.it
Notate che da linea di comando passiamo la macchina su cuio e' in esecuzione
il server. in questo esempio profs.sci.univr.it (voi sostiutitelo con la macchina
opportuna).
Se tutto va bene vi apparira' la scritta dovuta all'invocazione del metodo
remoto echo:
Io sono l'oggetto remoto con naming JNDI!!!!