ここで紹介するサーバーは、サーバントとサーバーの 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 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 機能を継承します。
まず、ssetORB(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() メソッドの呼び出しは、サーバーのコマンド行引数に渡されるので、実行時に特定のプロパティーを設定できます。
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 オブジェクトを関連付けることができます。
次のように、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 オブジェクトとして使うには、適切な型にナロー変換する必要があります。narrow() の呼び出しは、前の文の直後にあります。
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
これは、idlj により生成されるヘルパークラスの使用方法です。このクラスの機能は HelloHelper の機能に類似しています。ここで ncRef オブジェクトは org.omg.CosNaming.NamingContextExt になったので、次のステップで指示されるように、これを使ってネームサービスにアクセスし、サーバーを登録することができます。
NamingContextExt オブジェクトは 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() の中にあるため、呼び出しが終了して sayHello() が復帰したのち、サーバーはふたたび呼び出し待ちに戻ります。タスクの終了後に HelloClient
が明示的に ORB をシャットダウンするのはこのためです。
ここで HelloServer.java をコンパイルし、エラーを修正してからレッスンを続けます。
Windows のユーザーの方は、このマニュアルのパスのスラッシュ (/) をバックスラッシュ (\) に置き換えてください。
HelloServer.java をコンパイルするには、次のようにします。
javac HelloServer.java HelloApp/*.java
「Hello World アプリケーションの実行」では、HelloServer と残りのアプリケーションの実行について述べています。
CORBA は IDL インタフェースを実装するサーバー側マッピングのうち、少なくとも次の 2 種類をサポートしています。
継承モデルを使って、コンパイラが作成したスケルトンの拡張も行う実装クラスを使い、IDL インタフェースを実装します。
継承モデルには、次のものが含まれています。
-fall または -fserver のどちらかの引数が使用されるときに生成されるデフォルトのサーバー側マッピングは、CORBA 2.3.1 仕様の第 11 章「POA (Portable Object Adapter)」(formal/99-10-07) に準拠しています。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 ベースのサーバー側マッピングが生成されます。