MokaByte Numero  45 - Ottobre 2000
RMI IIOP 
II parte: la pratica
di 
Stefano Rossini
In questo articolo si parla di Rmi -IIOP : il primo punto d'incontro tra Rmi e Corba 

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à  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

 

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

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it