Esempio
pratico
Riprendiamo
l’esempio presentato nell’articolo di Networking dedicato a RMI [1] e sviluppiamolo
in chiave RMI-IIOP e IDL.
Questo
semplice esempio prevede di mettere a disposizione un servizio (oggetto
remoto), registrato con nome logico MyService, che permetta il banale computo
di restituire come risultato la concatenazione tra due stringhe ricevute
come parametri d'ingresso.
Lo
scopo è di verificare la completa compatibilità tra RMI e
RMI-IIOP con protocollo JRMP e la possibilità di colloquio tra RMI-IIOP
e Corba con protocollo IIOP.
|
Figura
2
Le
differenze tra un’applicazione Server RMI e RMI–IIOP sono:
-
Import
statement: bisogna aggiungere gli import relativi alla classe PortableRemoteObject
e alla classe JNDI InitialContext :
import
javax.rmi.PortableRemoteObject;
import
javax.naming.InitialContext;
-
Implementazione
dell’oggetto remoto: la classe “remota” eredita non più da UnicastRemoteObject
bensì da PortableRemoteObject, implementando la medesima interfaccia
remota RMI.
-
Registrazione
dell’oggetto remoto : associazione oggetto remoto con nome logico presso
il Repositary.
Non
si utilizza più il metodo statico bind o rebind della classe
Naming :
java.rmi.Naming.rebind("MyService",
objRef);
bensì
si istanzia un oggetto di classe InitalContext e se ne invoca il metodo
bind o rebind :
Context
initialNamingContext = new InitialContext();
InitialContext.rebind("Myservice",
objRef);
-
Mentre
con RMI si utilizza rmic al fine di ottenere stub e skeleton, con RMI-IIOP
si utilizza la nuova versione di rmic specificando flag –iiop per generare
lo stub ed il Tie.
-
Si avvia
il Naming Service tnameserv e non più RmiRegistry
Le differenze
tra un’applicazione Client Rmi e Rmi–IIOP sono :
-
import
di javax.rmi.PortableRemoteObject e javax.naming.InitialContext
come nel Server
-
Per ottenere
il riferimento all’oggetto remoto non si invoc il metodo statico della
classe Naming eseguendo un CAST esplicito alla RemoteInterface
MyServerInterface
serverRef
=(MyServerInterface)Naming.lookup(
rmi://server_name:1099/MyService);
bensì
si invoca il metodo lookup su un oggetto di classe InitialContext. Il risultato
è un oggetto di classe java.lang.Object. A questo Object ottenuto
dobbiamo invocare il metodo narrow() della classe PortableRemoteObject.
Il
metodo narrow è un metodo statico che prende un reference di un
oggetto o di un'interfaccia ed effettua un downcast al tipo di dato
richiesto.Se l'operazione ha successo il risultato sarà un oggetto
del tipo specificato, atrimenti verrà lanciata un'eccezione.
Context
initialNamingContext = new InitialContext();
Object
obj = initialNamingContext.lookup("MyService");
MyServerInterface
serverRef = (MyServerInterface)PortableRemoteObject.narrow(obj, MyServerInterface.class);
-
Infine
si genera lo stub con : rmic -iiop
La
figura 2 è la corrispettiva della figura 1 pubbicata nell'articolo
precedente (MokaByte 44 - Settembre 2000) in chiave Rmi-IIOP.
|
Figura
3
Vediamo
ora il codice nel dettaglio evidenziando i punti sopra descritti :
Interfaccia
Remota
L’interfaccia
è sempre la stessa , sia per RMI che per RMI-IIOP
public
interface MyServerInterface extends java.rmi.Remote {
public
String concat(String a, String b) throws java.rmi.RemoteException;
}
Implementazione
Interfaccia Remota
L’implementazione
dell’interfaccia cambia a secondo della tecnologia che si usa.
RMI
package
hellopackage;
import
java.rmi.*;
import
java.rmi.server.*;
public
class MyServerImpl extends UnicastRemoteObject implements MyServerInterface
{
public
MyServerImpl() throws RemoteException {}
public
String concat(String a, String b) throws RemoteException {
return "JRMP:" + a + b;
}
}
RMI-IIOP
package
hellopackage;
import
java.rmi.*;
import
javax.rmi.PortableRemoteObject;
public
class MyServerImpl extends PortableRemoteObject implements MyServerInterface
{
public
MyServerImpl() throws RemoteException {}
public
String concat(String a, String b)throws java.rmi.RemoteException {
return "IIOP:" + a + b;
}
}
Generazione
Stub e Skeleton
Dopo
avere compilato sia l’interfaccia che l’implememtazione, si procede a generare
i files di stub e skeleton in modo automatico mediante l’utilizzo del tool
rmic.
RMI
rmic
hellopackage.MyServerImpl
Otteniamo
i seguenti files :
MyServerImpl_Stub.class
MyServerImpl__Skel.class
RMI
– IIOP
rmic
-iiop hellopackage.MyServerImpl
Otteniamo
:
Stub:
_MyServerImpl_Stub.class
Skeleton
: _MyServerImpl_Tie.class
Server
RMI
package
hellopackage;
import
java.rmi.RMISecurityManager;
public
class RmiServer {
public RmiServer() {
try {
if
( System.getSecurityManager() == null )
System.setSecurityManager (new RMISecurityManager());
MyServerImpl server = new MyServerImpl();
java.rmi.Naming.bind("MyService", server);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
new RmiServer();
}
}
RMI-IIOP
package
hellopackage;
import
javax.naming.*;
import
java.rmi.RMISecurityManager;
public
class RmiIIOPServer {
public RmiIIOPServer() {
try {
if(System.getSecurityManager() == null )
System.setSecurityManager (new RMISecurityManager());
MyServerImpl server = new MyServerImpl();
Context initialNamingContext = new InitialContext();
initialNamingContext.rebind("MyService", server);
} catch (Exception e) {
e.printStackTrace();
}
}
public
static void main(String args[]) {
new RmiIIOPServer();
}
}
Attivazione
del Naming service
Prima
di mandare in esecuzione il server bisogna sempre mandare in esecuzione
il Repositary :
RMI
Esempi
di attivazione RmiRegistry :
Rmiregistry
(attivazione sulla porta TCP di default 1099)
Rmiregistry
6666 (attivazione sulla porta TCP 6666)
RMI
– IIOP
Esempi
di attivazioni tnameserv :
tnameserv
(attivazione sulla porta TCP di default 900)
tnameserv
-ORBInitialPort 7777 (attivazione sulla porta TCP 7777)
Funzionamento
del Server con protocollo JRMP (comando da eseguire su un'unica riga)
java
-Djava.security.policy=policy
-Djava.naming.factory.initial=
com.sun.jndi.rmi.registry.RegistryContextFactory
<ServerApplication>
Funzionamento
del Server con protocollo IIOP (comando da eseguire su un'unica riga)
java
-Djava.security.policy=policy
-Djava.naming.factory.initial=
com.sun.jndi.cosnaming.CNCtxFactory
<ServerApplication>
Client
Rmi
public
class RmiClient {
static String rmiHost;
public static void main(String[] args) {
String strLocalName=null;
if(args.length == 1)
rmiHost = args[0];
else
rmiHost = "rmi://localhost:1099/MyService";
try {
if ( System.getSecurityManager() == null )
System.setSecurityManager (new RMISecurityManager());
MyServerInterface serverRef =
(MyServerInterface)Naming.lookup(rmiHost);
String strResult = serverRef.concat("Hello"," World !");
System.out.println(strResult);
…
Rmi-IIOP
public
class RmiIIOPClient {
public
static void main(String[] args) {
try
{
if ( System.getSecurityManager() == null ) {
System.setSecurityManager(new RMISecurityManager());
String strFactory;
strFactory = System.getProperty("java.naming.factory.initial");
String msg="RmiIIOPClient::constructor=java.naming.factory.initial= ";
System.out.println(msg+strFactory);
String strUrl = System.getProperty("java.naming.provider.url");
msg"RmiIIOPClient::constructor=java.naming.provider.url= ";
System.out.println(msg+strUrl);
Context initialNamingContext = new InitialContext();
Object obj = initialNamingContext.lookup("MyService");
MyServerInterface serverRef =(MyServerInterface)
ortableRemoteObject.narrow(obj,MyServerInterface.class);
String strResult = serverRef.concat("Hello"," World !");
System.out.println(strResult);
…
CORBA
La
figura 4 è la corrispettiva delle figure 1 e 3 in chiave Rmi-IIOP.
|
Figura
4
Per
sviluppare un'applicazione Java IDL sfruttando il mapping Java-IDL introdotto
con Corba 2.3, i files di partenza sono gli stessi di RMI-IIOP.
Dato
il file MyServerInterface.class ed il file MyServerImpl.class, possiamo
ottenere un file IDL. Questo mapping al contrario, cioè da Java
a IDL, lo si ottiene facendo :
rmic
-idl -d idl hellopackage.MyServerImpl
Otteniamo
come risultato il seguente file IDL nella directory idl :
/**
*
hellopackage/MyServerInterface.idl
*
Generated by rmic -idl. Do not edit
*
sabato 5 agosto 2000 23.50.01 GMT+02:00
*/
#include
"orb.idl"
#ifndef
__hellopackage_MyServerInterface__
#define
__hellopackage_MyServerInterface__
module
hellopackage {
interface MyServerInterface {
::CORBA::WStringValue concat(
in ::CORBA::WStringValue arg0,
in ::CORBA::WStringValue arg1 );
};
#pragma
ID MyServerInterface "RMI:hellopackage.MyServerInterface:0000000000000000"
};
#endif
Fondamentalmente
è avvenuto il seguente mapping Java - IDL :
Java
Statement
|
IDL
Statement
|
package
hellopackage;
|
module
hellopackage
|
public
interface MyServerInterface
|
interface
MyServerInterface
|
public
String concat(String a, String b)
throws
java.rmi.RemoteException;
|
::CORBA::WStringValue
concat(
in
::CORBA::WStringValue arg0,
in
::CORBA::WStringValue arg1 );
}; |
A questo
punto creiamo i files Java partendo dal f ile IDL ottenuto tramite il tool
idlj
idlj
-Fall hellopackage\MyServerInterface.idl
Ottengo
files per il client-side :
_MyServerInterfaceStub
MyServerInterface
MyServerInterfaceHelper
MyServerInterfaceHolder
MyServerInterfaceOperations
e
quelli utili server-side
_MyServerInterfaceImplBase
Creiamo
la classe IdlServer al fine di registrare un oggetto di classe MyServant
con nome logico MyService.
package
hellopackage;
import
org.omg.CosNaming.*;
import
org.omg.CosNaming.NamingContextPackage.*;
import
org.omg.CORBA.*;
public
class IdlServer{
public static void main(String args[]){
try{
ORB orb = ORB.init(args, null); // inizializza l'ORB
MyServant helloRef = new MyServant(); // creazione del Servant
orb.connect(helloRef); // registrazione del Servant
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// Bind dell'ogetto helloRef con nome logico MyService
NameComponent nc = new NameComponent("MyService", "");
NameComponent path[] = {nc};
ncRef.rebind(path, helloRef);
java.lang.Object sync = new java.lang.Object();
// Attesa di invocazione dal client
synchronized(sync){ sync.wait(); }
}
catch(Exception
e) {
e.printStackTrace(System.out);
}
}
}
La
classe Servant eredita dalla classe _MyServerInterfaceImplBase, la quale
è stata creata dal tool idlj.
class
MyServant extends _MyServerInterfaceImplBase{
public
String concat(String a, String b) // NO : throws java.rmi.RemoteException
{
return "IDL:" + a + b;
}
}
Creo
il file IdlClient per invocare l’oggetto remoto di nome logico “Myservice”
:
package
hellopackage;
import
org.omg.CosNaming.*;
import
org.omg.CORBA.*;
public
class IdlClient{
public static void main(String args[]){
try{
ORB orb = ORB.init(args, null);
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
NameComponent nc = new NameComponent("MyService", "");
NameComponent path[] = {nc};
MyServerInterface helloRef =
MyServerInterfaceHelper.narrow(ncRef.resolve(path));
String Hello = helloRef.concat("Hello"," World !");
System.out.println(Hello);
} catch(Exception e) {
e.printStackTrace(System.out);
}
}
}
A questo
punto si possono compilare i files e avviare il tnameserv.
Verifiche di compatibilità
Dopo
la descrizione del codice del client e del Server RMI, RMI-IIOP e
IDL vediamo come eseguire le prove di compatibilità.
Ogni
tabella riporta la descrzione del test che viene eseguito mentre i punti
numerati indicano :
1.
Avvio del Naming Server
2.
Avvio dell'applicazione Server
3.
Avvio dell'applicazione client
4.
Il
file di policy (myjava.policy) usato per gli esempi non pone nessun vincolo
di sicurezza per semplificare l'esecuzione degli esempi.
//
Standard extensions get all permissions by default
grant
codeBase "file:${java.home}/lib/ext/-" {
permission java.security.AllPermission;
};
//
default permissions granted to all domains
grant
{
permission java.security.AllPermission;
};
Client
|
Name
Server
|
Server
|
Protocollo
|
Tcp
port
|
Rmi
IIOP
|
RmiRegistry
|
RmiServer
|
JRMP
|
1099
|
1.
start rmiregistry
2.
java -Djava.security.policy=myjava.policy
-Djava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
hellopackage.RmiIIOPServer
3.
java -Djava.security.policy=myjava.policy hellopackage.RmiClient rmi://localhost/MyService
Client
|
Name
Server
|
Server
|
Protocollo
|
Tcp
port
|
RmiClient
|
RmiRegistry
|
RmiIIOPServer
|
JRMP
|
1099
|
1.
start tnameserv
2.
java -Djava.security.policy=myjava.policy
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=iiop://localhost:900
hellopackage.RmiIIOPServer
3.
java -Djava.security.policy=myjava.policy
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-
-Djava.naming.provider.url=iiop://localhost:900
hellopackage.RmiIIOPClient
Client
Name Server Server Protocollo Tcp port
IdlClient
tnameserv IdlServer IIOP 900
1.
start tnameserv
2.
java hellopackage.IdlServer
3.
java hellopackage.IdlClient
Client
|
Name
Server
|
Server
|
Protocollo
|
Tcp
port
|
RmiIIOPClient
|
tnameserv
|
RmiIIOPServer
|
IIOP
|
900
|
1.
start tnameserv
2.
java -Djava.security.policy=myjava.policy
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
-Djava.naming.provider.url=iiop://localhost:900
hellopackage.RmiIIOPServer
3.
java hellopackage.IdlClient
Client
|
Name
Server
|
Server
|
Protocollo
|
Tcp
port
|
IdlClient
|
tnameserv
|
IdlServer
|
IIOP
|
900
|
1.
start tnameserv
2.
java hellopackage.IdlServer
3.
java -Djava.security.policy=myjava.policy
-Djava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
-Djava.naming.provider.url=rmi://localhost
hellopackage.RmiIIOPClient
Conclusioni
RMI-IIOP
è una versione migliore dell'originale RMI, perchè offre
la possibilità di interfacciarsi con il mondo Corba, opportunità
che con RMI viene preclusa.
L'utilizzo
di API JNDI conferisce flessibilità di configurazione di protocollo
permettendo di scegliere tra JRMP o IIOP.
RMI-IIOP
è già una realtà affermata nel mondo Java, e lo dimostra
il suo impiego con EJB.
Bibliografia
[1]
S.Rossini - "Networking in Java Parte V : RMI", Mokabyte N43., Luglio-Agosto
2000
[2]
L.Bettini-"Java+IDL=CORBA",Mokabyte N.22,Settembre 1998
[3]
A. Bemporad – “GIOP/IIOP l’autostradaCorba”, Dicembre 1998
[4]
M. Risso – “Java 2, Programmazione distribuita”, Apogeo 1999
[5]
A.Andoh -S.Nash – “Rmi over IIOP”, JavaWorld N12, Dicembre 1999
[6]
S. D’Amore – “L’interfaccia JNDI del JDK 1.2” N.42, Giugno 2000 |