ここで紹介するサーバーは、サーバントとサーバーの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 and 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つの方法は、ファイルへのオブジェクト参照を文字列化することです。
ネーム・サービスには、次の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ベースのサーバー側マッピングが生成されます。