Con RMI i progettisti della sun hanno aggiunto a Java un potente strumento per la realizzazione di applicazioni distribuite. Caratteristica principale di RMI è la semplicità il suo punto di forza ma anche maggiore limitazione. La possibilità di interfacciarsi con ORB di tipo CORBA è sicuramente una delle caratteristiche più interessanti del nuovo RMI IIOP
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 – Schema architetturale RMI-IIOP
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:
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 invoca il metodo statico della classe Naming eseguendo un CAST esplicito alla RemoteInterface
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, altrimenti verrà lanciata un'eccezione.
La figura 2 è la corrispettiva della figura 1 pubblicata nell'articolo precedente (MokaByte 44 - Settembre 2000) in chiave Rmi-IIOP.
Figura 3 – Schema RMI-IIOP dettagliato
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'implementazione, si procede a generare i files di stub e skeleton in modo automatico mediante l'utilizzo del tool rmic.
La figura 4 è la corrispettiva delle figure 1 e 3 in chiave Rmi-IIOP.
Figura 4 – Schema CORBA IDL
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 file 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'oggetto 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":
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 descrizione 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
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;
};
Test 1: Client RMI-IIOP con Server RMI su RmiRegistry
| Client | Name Server | Server | Protocollo | Tcp port | |--------|-------------|--------|------------|----------| | Rmi IIOP | RmiRegistry | RmiServer | JRMP | 1099 |
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'autostrada Corba", 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
MokaByte®
è un marchio registrato da MokaByte s.r.l.
Java®, Jini® e tutti i nomi derivati sono marchi
registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione
anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it