目次


エンタープライズ・レベルのRMI-IIOP

入門: IIOPをベースにしてRMIを実行する

Comments

IBMとSun Microsystemsは、エンタープライズ・レベルの開発テクノロジーとしてJavaを推進するために、共同のイニシアチブを1997年に立ち上げました。この2社が特に焦点を合わせていたのは、Javaをサーバー・サイド言語として採用し、既存のアーキテクチャーに組み込めるようなエンタープライズ・レベルのコードを生成するための方法です。そのために必要だったのは、JavaのRMI (Remote Method Invocation) の軽量さと、さらに完成度の高いCORBA (Common Object Request Broker Architecture) の堅牢さとを組み合わせた、リモート・トランスポート・テクノロジーでした。このようなニーズから生まれたRMI-IIOPは、Javaがエンタープライズ・レベルのサーバー・サイド開発の主要な言語として、現在のような位置を占めるようになった大きな要因でもあります。

エンタープライズ・レベルの開発ソリューションにRMI-IIOPを取り入れるきっかけになればという思いから、この記事では、RMI-IIOPの基本から説明していきますが、このRMI-IIOPの実像を伝えるために、実はCORBAとRMIの一般的な説明では見落とされがちな情報を取り上げておく必要があると思います。ちなみに、CORBAとRMIの基本については、参考文献にある記事やチュートリアルを参照してください。

RMI-IIOPの具体的な説明に入る前に、要求をマーシャリングするためのCORBAとRMIのメカニズムを見ておきましょう。主に取り上げるのはCORBAになります。RMI-IIOPのマーシャリングは、CORBAの通信プロトコル (IIOP) をベースにしているからです。要求を送信し、リモート・オブジェクトを検索して、そのオブジェクトをネットワーク上でやり取りする処理において、このIIOPプロトコルとORB (Object Request Broker) が果たす基本的な役割をこれから簡単に説明していきます。

リモート・オブジェクトのやり取り

CORBAの要求のマーシャリングは、IIOPプロトコルをベースにして実行されます。簡単に言えば、IIOPとは、IDL (Interface Definition Language) 構造の要素を、標準化された形式のバイト系列として記述するためのきまりです。たとえば、JavaクライアントがCORBAの要求をC++ サーバーに送る場合、クライアント・アプリケーションは、Javaインターフェースの形でリモート・オブジェクトの参照を保持し、そのインターフェースに対して何かの操作を呼び出します。水面下では、そのインターフェースがその操作の対応する実装を呼び出します。その実装は、スタブの中にあります (そのスタブは、IDLからidlj によって生成されたものです)。

スタブは、メソッド呼び出しをORBに送ります。ORBは、クライアントORBとサーバーORBという2つの部分に分かれています。クライアントORBの仕事は、ネットワーク上の指定の位置に向けて要求をマーシャリングすることであり、サーバーORBの仕事は、ネットワークからやって来る要求を受け取って、言語実装が理解できるメソッド呼び出しに変換することです。CORBA ORBの役割の詳細については、参考文献を参照してください。

クライアントORBは、スタブからメソッド呼び出しを受け取ると、その要求 (すべてのパラメーターを含む) を、標準化されたバイト形式 (この場合はIIOP) に変換し、ネットワーク経由でサーバーORBに送ります。サーバーORBは、データのバイト列を読み込んで、その要求をC++ サーバー実装が理解できる形に変換します。C++ サーバーは、要求されたメソッドを呼び出して、結果をクライアントに返します。返すときにも、ベースになるのはIIOPであり、同じメカニズムが使われます。

RMIの場合も、要求の処理は基本的に同じですが、通信プロトコルとしてJRMP (Java Remote Messaging Protocol) を使います。さらに、RMIのやり取りでは、Javaオブジェクトの直列化(serialization)機構が使われます。

リモート・オブジェクトの検索

CORBAは、リモート・オブジェクトの検索のために、CosNamingというネーム・サービスを使います。CosNamingは、ネーム・サーバーがCORBAサーバー・プロセスのバインディング (あるいは参照) を保持するためのしくみです。CORBAクライアントがCosNamingネーム・サービスに対し、サーバー・プロセスの名前を指定してCosNaming要求を送ると、CosNamingネーム・サービスは、そのプロセスのIOR (Interoperable Object Reference) を返します。クライアントは、そのIORを使って、サーバー・プロセスと直接通信することになります。

IORには、サーバー・プロセスの位置などの情報が入っていますが、CosNamingサービスのデメリットは、IORが人間に読めない形式で書かれていることです (少なくとも、サイボーグの頭脳を持たない人間には読めません !)。一方、RMIは、もう少しユーザー・フレンドリーです。RMIでは、JNDIの上に置いた「レジストリー」(ネーム・サービスのようなもの) でリモート・オブジェクトを検索します。RMIのレジストリーがリモート・オブジェクトの検索のために使用しているのは、JavaのReference オブジェクトであり、このオブジェクトは、いくつかのRefAddr オブジェクトで構成されています。これらのJavaオブジェクトは、IORよりもユーザー・フレンドリーです。

COBRAのオブジェクト検索のしくみには、最近になってINS (Interoperable Naming Service) が組み込まれました。INSは、CosNamingをベースにしており、人間が読める形のURLでオブジェクトの位置を示し、また、ネーム・サービスを利用しないで、指定されたURLに呼び出しを直接送ります。INSの詳細については、参考文献を参照してください。

RMIとCORBAの比較

というわけで、CORBAとRMIはどちらが優れているのでしょうか。その答えは、「場合によりけり」です。CORBAは、すでに定評のある堅牢なアーキテクチャーであり、業界標準の第3、第4世代のプロトコルをベースにしています。CORBAが提供しているアドオン (トランザクション処理、セキュリティー用のインターセプター、イベント・チャネルなど) のことを考えれば、エンタープライズ・レベルのアプリケーションにはCORBAが適しているとも言えそうです。しかし、CORBAには、複雑だという大きな欠点があり、CORBAをマスターするには、相当な勉強が必要になります。

一方、RMIをマスターするのは、非常に簡単です。RMIをベースにして、クライアント / サーバー型の実装を作成し、レジストリーやリモート・オブジェクトにバインドし、要求をやり取りする作業は、いたってシンプルです。さらに、ベースになるプロトコルを比較しても、RMIのJRMPはCORBAのIIOPよりかなり低コストなので、RMIはCORBAよりはるかにサイズ(footprint)の小さなものになっています。とはいえ、RMIには、CORBAのような強力なアドオンがなく、Java以外には対応できないという欠点があります。それで、欲を言えば、RMIの柔軟性と使いやすさに、CORBAのエンタープライズ・レベルの堅牢さを組み合わせたようなものが望ましいのではないでしょうか。そこで登場したのがRMI-IIOPだというわけです。

RMI-IIOPの概要

RMI-IIOPを活用すれば、ごくわずかな修正だけで、IIOPベースのRMI呼び出しを実行できます。単純明快なJavaコードで、CORBAが提供するエンタープライズ・レベルの豊富な機能を活用できます。コードの柔軟性が高いため、RMIをベースにして実行することも、IIOPをベースにして実行することも可能です。つまり、サイズが小さいことと柔軟性を重視する場合は、pure Java環境でそのまま実行でき、既存のCORBAインフラに組み込む場合も、コードをわずかに修正するだけで対応できるというわけです。

RMI-IIOPの非常に強力な特徴の1つは、RMIのクラス直列化の柔軟性を失わずに、pure Javaのクライアント / サーバー型実装を作成できるということです。そのために、RMI-IIOPでは、Javaの直列化をオーバーライドして、Javaクラスをネットワーク上でのIIOPに変換します。相手側では、ネットワークから出たところでJavaクラスがIIOPとして読み込まれ、Javaクラスの新しいインスタンスが (リフレクションによって) 生成されます (そのメンバーのすべての値はそのまま残ります)。こうして、IIOPベースのJava直列化を見事に実現しているわけです。

RMI-IIOPで透過的なオブジェクト検索を実現するために、ORBベンダーはこれまで、JavaのCosNamingというサービス・プロバイダー(俗に言う、「プラグイン」) を利用してきました。このプラグインは、JNDI APIの下で動作し、CORBAのネーム・サービスにアクセスする役割を果たします。ところが、このようなネーム・サービスのソリューションには、いろいろな問題があります(その詳細については、ここで詳しく取り上げる余裕がありません)。その結果、数多くのベンダー(特にアプリケーション・サーバーのベンダー) は、RMI-IIOPに対応するために独自のオブジェクト検索メカニズムを開発してきました。

RMI-IIOPは、JavaのCosNamingサービスの拡張機能であるINSもサポートしています。個人的には、このINSが今後のオブジェクト検索の方向性を示しているように思いますので、この記事のサンプル・コードでもINSを利用しています。

注: Sunは今のところ、OMGのINS標準規格に十分に対応しておらず、org.omg.CORBA.ORB インターフェースのregister_initial_reference メソッドを公開していないので、Sun JDKでこの記事のソース・コードを実行することはできません。ソース・コードの実行には、IBM Developer Kit for Java technologyバージョン1.3.1以上が必要です。ただし、ネーム・サービスを使用するSun互換のサンプルも作成してありますので、そのサンプルのダウンロードについては、参考文献をご覧ください。

自前のRMI-IIOPアプリケーション

説明はこれくらいにして、実際にコードを書いてみましょう。ここでは、シンプルなJavaベースのクライアント / サーバー型RMI-IIOPアプリケーションを構築します。このアプリケーションは、RMIインターフェース、サーバー・アプリケーション、クライアント・アプリケーションという3つの部分で構成されます。IIOPベースのJava直列化を実装しているので、Javaクラスがクライアントでインスタンス化され、サーバーに渡され、サーバーで変更され、クライアントに返されるという流れの中で、変更内容が無傷で残るようすを見てみましょう。

パート1: インターフェースの定義

RMI-IIOPでは、インターフェースの定義にRMIとIDLのどちらかを使用できますが、ここでは、IIOPベースのRMIの動作を見るために、RMIを使うことにします。リスト1は、この簡単なサンプルのRMIインターフェースです。

リスト1. RMIInterface.java
/*
 * Remote interface
 */
public interface RMIInterface extends java.rmi.Remote {
    public String hello() throws java.rmi.RemoteException;
    public SerClass alterClass(SerClass classObject) 
       throws java.rmi.RemoteException;
}

RMIInterface では、hello() メソッドとalterClass(SerClass) メソッドを定義しています。このalterClass(SerClass) メソッドの引数であるSerClass は、Serializable を実装したJavaクラスであり、同じタイプのクラスを返します。ほんのいくつかのメンバーと、それぞれに対応するゲッター・メソッドだけからなるシンプルなクラスです。このクラスのメソッドをリスト2に示します。

リスト2. SerClass.java
/**
 *  This class is intended to be serialized over RMI-IIOP.
 */
public class SerClass implements java.io.Serializable {
	// members
	private int x;
	private String myString;

	// constructor
	public SerClass(int x, String myString) 
	   throws java.rmi.RemoteException {
		this.x=x;
		this.myString=myString;
	} 
	
	// some accessor methods
	public int getX() {  return x;}
	public void setX(int x) { this.x=x; }
	public String getString() {  return myString;  }
	public void setString(String str) { myString=str; }
}

このシンプルなインターフェースは、これですべてです。次に、サーバー・クラスに進みましょう。

パート2: サーバーの構築

ここで使うサーバー・クラス (Server.java) は、RMIInterface の実装クラスとして動作し、サービスを開始するためのmain メソッドを含んでいます。javax.rmi.PortableRemoteObject を拡張したものなので、ORBに対するRemote インターフェースとして自らをバインドし、要求を受け取るために必要なすべての機能が組み込まれています。リスト3は、サーバーのコードです。

リスト3. Server.java
/*
 * Simple server
 */
import java.util.*;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import javax.rmi.CORBA.Tie;
import javax.rmi.CORBA.Util;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.*;
import org.omg.PortableServer.Servant;
import org.omg.CORBA.ORB;

public class Server extends PortableRemoteObject 
    implements RMIInterface {
    // must explicitly create default constructor 
    // to throw RemoteException
    public Server() throws RemoteException {
    }

    // implementation of RMIInterface methods
    public String hello() throws RemoteException {
    	return "Hello there!";
    }

    public SerClass alterClass(SerClass classObject) 
        throws RemoteException {
    	// change the values of SerClass and return it.
	// add 5 to X
    	classObject.setX( 
    	   classObject.getX() + 5 ); 
	// alter the string
    	classObject.setString( 
    	   classObject.getString() + " : I've altered you" ); 
    	return classObject;
    }	

    public static void main(String[] args) {
    	try {
    	    // create the ORB passing in the port to listen on
    	    Properties props = new Properties();
    	    props.put("com.ibm.CORBA.ListenerPort","8080");
    	    ORB orb = ORB.init(args, props);
    
    	    // instantiate the Server
    	    // this will automatically call exportObject(this)
    	    Server s = new Server();
            
    	    // now get the Stub for our server object - 
         // this will be both
    	    // a remote interface and an org.omg.CORBA.Object
    	    Remote r=PortableRemoteObject.toStub(s);        		
    
    	    // register the process under the name 
         // by which it can be found	
    	    ((com.ibm.CORBA.iiop.ORB)orb).
    		register_initial_reference("OurLittleClient",
			(org.omg.CORBA.Object)r);
    
    	    System.out.println("Hello Server waiting...");
    	    // it's that easy - 
	    // we're registered and listening for incoming requests
    	    orb.run();
    	} catch (Exception e) {
    	    e.printStackTrace();
    	}
    }
}

サーバー・コードの分析

このサーバー・アプリケーションのコードはかなり長いので、分解しながら見ていきましょう。まず、すでに述べたとおり、Server クラスは、RMIInterface を実装したものであり、RMIInterface のすべてのメソッドの実装を提供しています。コードの最初のほうには、RMIInterfacehello() メソッドとalterClass(SerClass) メソッドの実装があります。hello() メソッドは、「Hello there!」という文字列を返すだけですが、alterClass(SerClass) メソッドは、SerClass オブジェクトを引数として取り、メンバーの値をすべて変更してから、新しいオブジェクトを返します (すべての処理のベースになっているのは、RMI-IIOPです)。

Server.javamain メソッドは、ORBを初期化して、com.ibm.CORBA.ListenerPort プロパティーのパラメーターを8080に設定します。これで、このORBは、ポート8080で要求を受け取ることになります。ただし、このcom.ibm.CORBA.ListenerPort は、IBM独自のプロパティーなので、このコードを別のベンダーのORBで実行する場合は、そのベンダーの資料から該当するプロパティーを調べてください。ちなみに、Sunは、com.sun.CORBA.POA.ORBPersistentServerPort を使っていますが、このプロパティーは、POA (ポータブル・オブジェクト・アダプター) サーバントの使用を前提としています。

main メソッドは、ORBを初期化してから、Server オブジェクトのインスタンスを生成します。このサーバー・オブジェクトは、PortableRemoteObject でもあるので、デフォルト・コンストラクターが自動的にexportObject(this) を呼び出します。この時点で、リモート呼び出しを受け取る準備ができました。

次に、ORB.register_initial_reference(String,orb.omg.CORBA.Object) を呼び出して、このサーバー・オブジェクトを登録する必要があります。そのためには、まず、このサーバー・オブジェクトをorg.omg.CORBA.Object として参照しなければなりません。その目的で呼び出すのが、PortableRemoteObject.toStub(s) です。このメソッドからは、java.rmi.Remoteorg.omg.CORBA.Object の両方を実装したオブジェクトが返されます。

ここで返されたorg.omg.CORBA.Object オブジェクトを、今度は、サーバー・サイドORBに「OurLittleClient」として登録します。登録のためには、register_initial_reference を呼び出します。これで、INS要求でそのオブジェクトの名前を指定できるようになりました。ORBは、INS呼び出しを受け取ると、指定されている名前の登録済みオブジェクトを探します。今回は「OurLittleClient」という名前でオブジェクトを登録しているので、クライアントからのINS呼び出しをサーバーORBに送るときに、「OurLittleClient」という名前を指定すればよいわけです。

最後に、ORBをcom.ibm.CORBA.iiop.ORB にキャストしている点に注目しておきましょう。Sunは、org.omg.CORBA.ORB インターフェースのregister_initial_reference メソッドをまだ公開していないので、IBM SDKでもそのメソッドを公開できない状態になっています。ここでORBをIBM ORBにキャストしているのは、そういうわけです。SunがOMG準拠をさらに進めれば、今後のバージョン (1.4.0より後) のJDKでは、こうしたキャストが不要になるかもしれません。

これで、サーバーは完成です。「超カンタン」という感じでしょうか。このサーバーは、クライアントからのINS要求に対応できる状態になったので、次は、そのクライアントを構築する番です。

パート3: クライアントの構築

クライアント・アプリケーションのコードをリスト4に示します。

リスト4. Client.java
/*
 * Client application
 */
import javax.rmi.PortableRemoteObject;
import org.omg.CORBA.ORB;

public class Client {
  public static void main(String[] args) {
    try {
      ORB orb = ORB.init(args, null);
    
    	 // here's the URL for the local host
    	 String INSUrl = 
        "corbaloc:iiop:1.2@localhost:8080/OurLittleClient";	 
       
    	 // get the reference to the remote process
    	 org.omg.CORBA.Object objRef=orb.string_to_object(INSUrl);
    	 // narrow it into our RMIInterface
    	 RMIInterface ri = 
  (RMIInterface)PortableRemoteObject.narrow(objRef, RMIInterface.class);
    	
      // call the hello method
    	 System.out.println("received from server: "+ri.hello()+"\n");  
    
      // try RMI serialization
    	 SerClass se = new SerClass(5, "Client string! ");
    	 // pass the class to be altered on the server
    	 // of course behind the scenes this class is being 
      // serialized over IIOP
    	 se = ri.alterClass(se);
    	 // now let's see the result
    	 System.out.println("Serialization results :\n"+
    	    "Integer was 5 now is "+se.getX()+"\n"+
    	    "String was \"Client String! \" 
         now is \""+se.getString()+"\"");   
    	} catch (Exception e) {
    	    e.printStackTrace();
    	}
    }
}

クライアント・コードの分析

クライアント・コードは、サーバー・コードよりシンプルになっています。まずORBを初期化して、string_to_object(String) を呼び出します (この場合のStringは、このサンプルのINS URLです)。そのINS URLを構築するのも簡単です。まず、corbaloc URL (参考文献を参照) とIIOPプロトコルのバージョン1.2を使用することを宣言してから、ホスト名 (www.whatever.com) と接続のためのポートを追加し、最後に、対象となるサービスの名前を指定するだけです。こうしてできあがったINS URLは、corbaloc:iiop:1.2@localhost:8080/OurLittleClient になります。

このURLをORB.string_to_object(String) に渡すと、ORBは、対象としているサービスに関する要求を指定のサーバーに送ります。すべてがうまくいけば、そのサービスのオブジェクト参照 (実際にはIOR) が返されるので、そのオブジェクト参照を、実際に使えるインターフェース (RMIInterface) の中に絞り込めば、メソッドを呼び出せる状態になるわけです。

シンプルなhello メソッド (これには説明は不要でしょう) を呼び出した後、いよいよRMI-IIOPの直列化機能のテストができます。まず、直列化が可能なJavaクラスであるSerClass を作成し、そのメンバーの変数を初期化します。次に、そのクラスをalterClass メソッドに渡すと、そのメソッドがIIOPをベースにしてサーバーにそのクラスを書き出します。サーバーは、そのクラスを読み込み、サーバー・サイドのJavaオブジェクトに作り直し、メンバーの値を変更し、alterClass メソッドの戻り値として結果を返します (もちろん、ベースになるのはIIOPです)。リモート・メソッド呼び出しの結果として、変換後のオブジェクトを受け取ると、サーバーによってメンバーが変更されているはずです。非常に簡単ですが、これがまさにIIOPベースのJava直列化というわけです。

パート4: サンプルの実行

ここで作成したサンプルは、IBM Developer Kit for Java technologyバージョン1.3.1以上で実行できるはずです。Sun JDKを使いたい場合は、 Sun互換のソース・コード

サンプルを実行するための手順は、次のとおりです。

  1. ソース・ファイル をダウンロードします。
  2. すべてのファイルに対して、javac を実行します (javac *.java と入力します)。
  3. サーバー・クラスに対して、IIOPフラグを付けたrmic を実行します (rmic -iiop Server と入力します)。
  4. サーバーを開始します (Windowsの場合は、start java Server と入力します)。
  5. クライアントを開始します (Windowsの場合は、start java Client と入力します)。

RMI-IIOPとEJBコンポーネントについて

EJB 2.0の仕様によれば、EJBコンポーネントは、RMIとRMI-IIOPの両方で実行できなければならないことになっています。このように、EJBコンポーネントの通信プロトコルとしてRMI-IIOPが追加された結果、CORBAに大きく依存している既存の企業インフラにJ2EE環境を組み込むことがかなり容易になりました。とはいえ、問題はまだ残っています。

簡単に言えば、企業の独自のコンポーネントとEJBコンポーネントを統合するには、かなりの下準備が必要だということです。統合ということを考えなければEJBアーキテクチャーによって抽象化されてしまうような作業が、開発者自身に求められるというわけです。今のところ、この問題に関する簡単な解決策はありませんし、今後も解決されないかもしれません。1つの可能性としてあるのは、Webサービスなどの新しいテクノロジーによる解決策ですが、これも現時点では不確実です。

結論: 今後の発展

この記事を読んで、RMI-IIOPのクライアント / サーバー型アプリケーションを構築して実行するのがとても簡単だということを理解していただけたとすれば、たいへん幸いです。このサンプルをいろいろいじって、クライアントかサーバーを純粋なCORBAに変えてみるのもおもしろいかもしれません。もっとも、その場合は、Java直列化機能がアプリケーションから消えることになります。

CORBA環境でRMI-IIOPを使う場合は、IDLとJavaの関係を整理してみるとよいでしょう。セキュリティーの弱い環境 (もちろん、読者のPCのことを言っているのではありません) でRMI-IIOPを使うのであれば、CORBAのセキュリティー機能 (インターセプター、CORBAセキュリティー・モデルなど) や、トランザクション処理をはじめとするエンタープライズ・レベルの機能を調べてみることをお勧めします。RMI-IIOPを実行すれば、CORBAの豊富な機能がすべて手に入るわけです。

この記事についてのコメントをお寄せください。著者に直接お送りいただければ幸いです。よろしくお願いいたします。


ダウンロード可能なリソース


関連トピック

  • IIOPプロトコルを策定したのは、Object Management Group (OMG) です。この団体は、CORBA仕様の開発と保守も行っています。
  • RMIの詳細については、RMIのサイトをご覧ください。
  • Java Developer Connectionでは、INSのチュートリアルを提供しています。このチュートリアルでは、ネーム・サービス、CosNamingサービス、corbaloc URL形式についても簡単に取り上げています。
  • Javaテクノロジーに関する選択肢を広げたい場合には、IBM Developer Kits for Java technology の完全なリストをご覧ください。
  • EJBテクノロジーとCORBAの関係については、Ken Nordby氏の「Enterprise JavaBeansコンポーネントの配置と使用」 (developerWorks、2000年7月) を参照してください。これは、EJBテクノロジーを紹介した3回シリーズの最後の記事です。
  • RMI-IIOPの全体像をつかむために、ServerSide.comの記事「RMI/IIOP, nice idea but the reality is turning out to be different」をご覧ください。この記事では、RMI-IIOPで実現できない事柄を取り上げています。
  • IBMdeveloperWorks のJava technologyゾーン には、Javaプログラミングをあらゆる角度から取り上げた記事が多数掲載されています。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=229406
ArticleTitle=エンタープライズ・レベルのRMI-IIOP
publish-date=03012002