ここで紹介するサーバは、サーバントとサーバの 2 つのクラスで構成されます。サーバントである HelloImpl は、Hello IDL インタフェースの実装です。つまり、Hello の各インスタンスは、HelloImpl のインスタンスにより実装されます。サーバントは、idlj コンパイラにより例の IDL から生成される HelloPOA のサブクラスです。
サーバントには、IDL 操作ごとに 1 つのメソッドが含まれます。この例では、sayHello() および shutdown() メソッドです。サーバントメソッドは、Java の通常のメソッドと変わりはありません。ORB の処理、引数や結果の整列化などを行うコードは、スケルトンで実装します。
サーバクラスにはサーバの main() メソッドが含まれます。 この main() メソッドでは、次の処理を行います。
このレッスンでは、CORBA サーバ作成の基本を学びます。持続オブジェクトサーバを備えた「Hello World」プログラムの例は、「例 2: 持続性を備えた Hello World」を参照してください。CORBA サーバの詳細については、「サーバの開発」を参照してください。
このレッスンの手順は次のとおりです。
HelloServer.java を生成するには、次のようにします。
// HelloServer.java // Copyright and License import HelloApp.*; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CORBA.*; import org.omg.PortableServer.*; import org.omg.PortableServer.POA; import java.util.Properties; class HelloImpl extends HelloPOA { private ORB orb; public void setORB(ORB orb_val) { orb = orb_val; } // implement sayHello() method public String sayHello() { return "\nHello world !!\n"; } // implement shutdown() method public void shutdown() { orb.shutdown(false); } } public class HelloServer { public static void main(String args[]) { try{ // create and initialize the ORB ORB orb = ORB.init(args, null); // get reference to rootpoa & activate the POAManager POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate(); // create servant and register it with the ORB HelloImpl helloImpl = new HelloImpl(); helloImpl.setORB(orb); // get object reference from the servant org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl); Hello href = HelloHelper.narrow(ref); // get the root naming context org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService"); // Use NamingContextExt which is part of the Interoperable // Naming Service (INS) specification. NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef); // bind the Object Reference in Naming String name = "Hello"; NameComponent path[] = ncRef.to_name( name ); ncRef.rebind(path, href); System.out.println("HelloServer ready and waiting ..."); // wait for invocations from clients orb.run(); } catch (Exception e) { System.err.println("ERROR: " + e); e.printStackTrace(System.out); } System.out.println("HelloServer Exiting ..."); } }
ここでは、HelloServer.java の各行について、コードが何をしているか、またこのアプリケーションでなぜ必要なのかについて説明します。
基本設定
CORBA サーバプログラムの構造は、ほとんどの Java アプリケーションと同じです。つまり、必要なライブラリパッケージをインポートし、サーバクラスを宣言し、main() メソッドを定義し、そして例外の処理を行います。
必要なパッケージのインポート
まず、サーバクラスに必要なパッケージをインポートします。
// The package containing our stubs import HelloApp.*; // HelloServer will use the naming service import org.omg.CosNaming.*; // The package containing special exceptions thrown by the name service import org.omg.CosNaming.NamingContextPackage.*; // All CORBA applications need these classes import org.omg.CORBA.*; // Classes needed for the Portable Server Inheritance Model import org.omg.PortableServer.*; import org.omg.PortableServer.POA; // Properties to initiate the ORB import java.util.Properties;
この例では、HelloServer.java 内の HelloServer クラスの外側に、サーバントオブジェクトのクラスを定義しています。
class HelloImpl extends HelloPOA { // The sayHello() and shutdown() methods go here. }
このサーバントは HelloPOA のサブクラスなので、コンパイラが HelloPOA のために生成した汎用の CORBA 機能を継承します。
まず、setORB(ORB)
メソッドで使用されるプライベート変数 orb
を作成します。setORB メソッドは、サーバントに ORB (値) を設定できるようにアプリケーション開発者により定義されるアプリケーション固有のメソッドです。この ORB 値は、クライアントからの shutdown() メソッドの呼び出しに応じて、固有の ORB 上で shutdown() を呼び出すために使用されます。
private ORB orb; public void setORB(ORB orb_val) { orb = orb_val; }
次に、必要な sayHello() メソッドを、宣言して実装します。
public String sayHello() { return "\nHello world!!\n"; }
最後に、shutdown()
メソッドを同様の方法で実装します。shutdown()
メソッドは、ORB 用に org.omg.CORBA.ORB.shutdown(boolean)
メソッドを呼び出します。shutdown(false)
操作は、ORB が処理の完了を待たずに、すぐにシャットダウンする必要があることを指示します。
public void shutdown() { orb.shutdown(false); }
次に、サーバクラスを宣言します。
public class HelloServer { // The main() method goes here. }
すべての Java アプリケーションには main メソッドが必要です。このメソッドを次のように、HelloServer クラスのスコープ内で宣言します。
public static void main(String args[]) { // The try-catch block goes here. }
どの CORBA プログラムでも、実行時に CORBA システム例外が発生する可能性があるので、main() メソッドの機能は、すべて try-catch ブロック内に記述します。CORBA プログラムは、呼び出しに伴うプロセス (整列化、非整列化、アップコール) で問題が発生すると、システム例外を発生させます。このレッスンの例外ハンドラは簡単なもので、どんな問題が起こったかが分かるように、例外の名前とそのスタックトレースを標準出力に出力します。
main() の中に、次の try-catch ブロックを記述します。
try{ // The rest of the HelloServer code goes here. } catch(Exception e) { System.err.println("ERROR: " + e); e.printStackTrace(System.out); }
CORBA サーバには、CORBA クライアントと同様にローカル ORB オブジェクトが必要です。各サーバは ORB のインスタンスを生成し、それが呼び出しを受けたときにサーバを検索できるように、そのサーバントオブジェクトを登録します。
try-catch ブロックの中で、ORB 変数を宣言して初期化します。
ORB orb = ORB.init(args, null);
ORB の init() メソッドの呼び出しは、サーバのコマンド行引数に渡されるので、実行時に特定のプロパティを設定できます。
ルート POA への参照の取得および POAManager の起動
ORB は、resolve_initial_references
メソッドを使用するネームサービスなどのサービスに対する初期のオブジェクト参照を取得します。
ルート POA への参照が取得され、POAManager が try-catch ブロックの中で起動されます。
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate();
activate()
操作は、POA マネージャの状態をアクティブに変更します。その結果、関連付けられた POA は要求の処理を開始します。POA マネージャは、関連付けられている POA の処理状態をカプセル化します。各 POA
オブジェクトには、1 つの POAManager
オブジェクトが関連付けられています。POA マネージャは、1 つ以上の POA オブジェクトに関連付けられていることがあります。
サーバントオブジェクトの管理
サーバとは、1 つ以上のサーバントオブジェクトのインスタンスを生成するプロセスです。サーバントは、idlj が生成したインタフェースから継承し、そのインタフェース上で実際の操作を行います。このレッスンの HelloServer には 1 つの HelloImpl が必要です。
サーバントオブジェクトのインスタンスの生成
次のように、POA マネージャを起動した直後に、try-catch ブロックの中でサーバントオブジェクトのインスタンスを生成します。
HelloImpl helloImpl = new HelloImpl();
サーバントクラスを記述するコードについては、すでに説明されています。
次のコード行は、ORB.shutdown() をシャットダウン操作の一部として呼び出せるようにするため、setORB(orb) はサーバントで定義されています。この手順が必要なのは、shutdown() メソッドが Hello.idl に定義されているからです。
helloImpl.setORB(orb);
シャットダウン操作の実装には、他の方法もあります。この例では、Object 上で呼び出された shutdown() メソッドは、ORB のシャットダウンを行います。別の実装例では、シャットダウンメソッドの実装はフラグを設定するだけで済みます。サーバがフラグを確認し shutdown() を呼び出します。
次の一連のコードは、サーバントに関連付けられたオブジェクト参照の取得に使用されます。narrow() メソッドは、CORBA オブジェクト参照を適切な型に変換するために必要です。
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl); Hello href = HelloHelper.narrow(ref);
HelloServer は COS (Common Object Services) ネームサービスを利用して、クライアント側からサーバントオブジェクトの操作を利用可能にします。サーバは、さまざまなインタフェースを実装しているオブジェクトの参照を発行できるようにするため、ネームサービスへのオブジェクト参照が必要です。これらのオブジェクト参照は、クライアントがメソッドを呼び出すのに使用されます。サーバントがオブジェクトをクライアント側から呼び出させることができるようにするもう 1 つの方法は、ファイルへのオブジェクト参照を文字列化することです。
J2SE v1.4 に付属しているネームサービスには、次の 2 つのオプションがあります。
この例では orbd を使用します。
初期ネーミングコンテキストの取得
try-catch ブロックの中で、サーバントのオブジェクト参照を取得すると、次に orb.resolve_initial_references() を呼び出してネームサーバへのオブジェクト参照を取得します。
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
文字列 「NameService」 は、すべての CORBA ORB に対して定義されています。この文字列を渡すと、ORB はネームサービスへのオブジェクト参照であるネーミングコンテキストオブジェクトを返します。文字列 NameService は、次のことを示しています。
独自の文字列 TNameService は、ORBD のネームサービスを使用するときは、一時ネームサービスとなることを示しています。
オブジェクト参照のナロー変換
CORBA のどのオブジェクト参照とも同様に、objRef は汎用の CORBA オブジェクトです。これを NamingContextExt object として使うには、適切な型にナロー変換する必要があります。narrow() の呼び出しは、前の文の直後にあります。
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
これは、idlj から生成されるヘルパークラスの使用方法です。それらは HelloHelper に機能的に類似しています。ここで ncRef オブジェクトは org.omg.CosNaming.NamingContextExt になったので、次のステップで指示されるように、これを使ってネームサービスにアクセスし、サーバを登録することができます。
NamingContextExt オブジェクトは、J2SE v1.4 で新たに追加されたもので、Interoperable Naming Service の仕様の一部です。
ネームサーバへのサーバントの登録
narrow() の呼び出しの直後に、新しい NameComponent 配列を作成します。NamingContext.resolve には作業用に配列が必要です。Hello オブジェクトへのパスには要素が 1 つしかないので、単一要素の配列を作成します。
String name = "Hello"; NameComponent path[] = ncRef.to_name( name );
path とサーバントオブジェクトをネームサービスに引き渡して、サーバントオブジェクトを 「Hello」id に結びつけます。
ncRef.rebind(path, href);
これで、クライアントが初期ネーミングコンテキストで resolve("Hello") を呼び出すと、ネームサービスから Hello サーバントへのオブジェクト参照が返されます。
前の節では、サーバを準備するためのコードの説明をしました。次の節では、クライアントがサービスを要求するのを待つコードについて説明します。try-catch ブロック内の最後にある次のコードは、これを実行するためのものです。
orb.run();
ORB.run() は、メインスレッドによって呼び出されると、ORB がそのメインスレッドを使って動作できるようになるため、ORB からの呼び出しを受け取るまで待機します。呼び出しは main() の try-catch ブロックの中にあるので 、呼び出しが終了して sayHello() が復帰したのち、サーバは再び呼び出し待ちに戻ります。タスクの終了後に HelloClient
が明示的に ORB をシャットダウンするのはこのためです。
ここで HelloServer.java をコンパイルし、エラーを修正してからレッスンを続けます。
Windows ユーザは、このマニュアルのパスのスラッシュ (/) をバックスラッシュ (\) に置き換えてください。
HelloServer.java をコンパイルするには、次のようにします。
javac HelloServer.java HelloApp/*.java
ドキュメント「Hello World アプリケーションの実行」では、HelloServer と残りのアプリケーションの実行について述べています。
CORBA は IDL インタフェースを実装するサーバ側マッピングのうち、少なくとも次の 2 種類をサポートしています。
継承モデルを使って、コンパイラが作成したスケルトンの拡張も行う実装クラスを使い、IDL インタフェースを実装します。
継承モデルには、次のものが含まれています。
J2SE v1.4 の新機能: -fall または -fserver のどちらかの引数を使用したときに生成されるデフォルトのサーバ側マッピングは、CORBA 2.3.1 仕様 (formal/99-10-07) の第 11 章 「Portable Object Adapter」(POA)に準拠しています。POA の詳細については、「ポータブルオブジェクトアダプタ」を参照してください。
POA (Portable Object Adaptor) を使用する利点は、次のとおりです。
注: ImplBase は POA モデルがあるので推奨されませんが、バージョン 1.3 以前の J2SE で記述されたサーバと互換性を持つために提供されています。これは非標準モデルなので、これを使って新しいサーバを作成することはお勧めしません。
委譲モデルを使い、次の 2 つのクラスを使って IDL インタフェースを実装します。
委譲モデルは、Tie モデルや Tie 委譲モデルとしても知られています。このモデルは POA または ImplBase コンパイラで作成されたスケルトンのどちらかを継承するので、このドキュメントでは POA/Tie または ImplBase/Tie モデルのように記述されます。
このチュートリアルでは、サーバ側実装の POA 継承モデルを扱います。他のサーバ側実装を使用するチュートリアルは、次のドキュメントを参照してください。
他の実装から継承しなければならない場合、標準の継承モデルではなく Tie モデルを使用することがあります。Java の場合は、インタフェースの継承の個数に制限はありませんが、クラスの継承に使用できるスロットは 1 つだけです。継承モデルを使用した場合は、そのスロットが占有されます。Tie モデルを使用した場合は、そのスロットが使用されず、ユーザが独自の目的で使用することができます。ただし、間接参照のレベルが 1 つ導入されるという欠点があります。つまり、メソッドを呼び出すときに余分なメソッド呼び出しが発生します。
ImplBase サーバ側のモデルは、POA モデルと同じく継承モデルです。idlj コンパイラにより、-oldImplBase フラグを使って J2SE 1.4 以前のバージョンの Java IDL と互換性があるサーバ側バインディングを生成します。
-oldImplBase フラグを使用するのは一般的ではありません。これらの API は推奨されていません。このフラグを使用するのは、J2SE 1.3 以前で書かれた既存のサーバとの互換性を取る場合だけです。その場合、既存の MAKEFILE を変更して idlj コンパイラに -oldImplBase フラグを追加する必要があります。フラグを追加しない場合、POA ベースのサーバ側マッピングが生成されます。
前のレッスン: インタフェース定義の記述
次のレッスン: クライアントアプリケーションの開発
チュートリアルのホーム
Java IDL トップへ |