このドキュメントは、CORBAによってサポートされている任意の言語で記述されたクライアントからEnterprise JavaBeansコンポーネント(EJBコンポーネント)にアクセスできるようにする方法を説明します。このドキュメントは、Java Platform, Enterprise Edition (Java EE)およびCORBA (Common Object Request Broker Architecture)に関する高度な知識をお持ちのプログラマを対象に書かれています。
Java EEテクノロジは、エンタープライズ・アプリケーションを単純化します。Java EEテクノロジにより、エンタープライズ・アプリケーションは、Enterprise JavaBeans (EJB)に基づいたモジュール型の再利用可能な標準コンポーネントに準拠するようになり、Java EEテクノロジから提供された完全なサービス・セットを利用できるようになります。アプリケーションの細かい動作の多くは、Java EEテクノロジによって自動的に処理されます。時間のかかる複雑なアプリケーション開発タスクの多くが自動化されるため、エンタープライズ開発者はインフラストラクチャの構築よりも価値の付加、つまりビジネス・ロジックの拡張に力を注ぐことができます。
EJBのサーバー側コンポーネント・モデルを使うと、トランザクションに適した、拡張性と移植性の高いミドルウェア・コンポーネントを簡単に開発できます。Enterprise JavaBeansサーバーを使用することで、トランザクション、セキュリティ、データベース接続性などのミドルウェア・サービスが自動的にサポートされるため、ミドルウェア開発の複雑さが軽減されます。
CORBAは、オブジェクト管理グループ(OMG)の規格の1つです。ベンダーに依存しないオープンなアーキテクチャと、複数のコンピュータ・アプリケーションがネットワーク経由で利用できるインフラストラクチャという特徴を備えています。CORBAベースのプログラムは、標準プロトコルであるIIOP (Internet Inter-ORB Protocol)を使って、他のCORBAベースのプログラムと連携できます。このとき、双方のプログラムの作成元ベンダーは同じでも異なっていてもかまいません。これらのプログラムが利用するコンピュータ、オペレーティング・システム、プログラミング言語、ネットワークについても、ほとんど制限はありません。CORBAの詳細は、Object Management GroupのWebサイトを参照してください。
CORBAテクノロジは分散オブジェクト・フレームワーク、そのフレームワークをサポートするサービス、および他言語との相互運用性を提供してJavaプラットフォームを補完します。CORBAテクノロジは、Java SEプラットフォームの重要な部分であり、Enterprise JavaBeansコンポーネント、標準プロトコルInternet Inter-ORB Protocol (「Java RMI-IIOP」)を利用して実行されているJava Remote Method Invocation API、およびJava IDL API (「Java IDL」)で使用されます。
OMG Interface Definition Language (IDL)は、リモート・オブジェクトによって実装されるインタフェースの説明に使用されます。IDLは、インタフェース名と個々の属性およびメソッドの名前の定義に使用されます。IDLファイルの作成後、IDLコンパイラを使ってクライアント・スタブとサーバー・スケルトンを生成できます。OMGで言語マッピングの仕様が定義されている言語であれば、どの言語でも使用できます。OMG IDLの詳細は、OMG IDLのWebサイトを参照してください。
Java IDLは、オブジェクト管理グループ(http://www.omg.org)によって定義された業界標準のOMG IDLおよびIIOPを使用して、分散Javaアプリケーションからリモート・ネットワーク・サービスを透過的に処理できるようにします。Java RMI over IIOP APIは、javax.rmi
APIを介したCORBAサーバーおよびアプリケーションのプログラミングを可能にします。
EJBコンポーネントのプログラミングを行う開発者は、分散オブジェクト・モデルとしてJava RMIプログラミング・モデルを使用します。このプログラミング・モデルでは、Java RMI-IIOPがすべてのアプリケーション・サーバーに共通の必須トランスポート・プロトコルになります。異機種サーバー混在環境では、EJBアーキテクチャとCORBAの標準マッピングにより、次のような相互運用性が実現されます。
このドキュメントの残りの部分では、エンタープライズBeanオブジェクトにアクセスするCORBAクライアント・アプリケーションの例を紹介します。このドキュメントでは、Javaプログラミング言語、C++、C、Smalltalk、COBOL、Ada、Lisp、またはPythonを含む任意のCORBAサポート言語で記述されたクライアント・アプリケーションを「CORBAクライアント」と呼びます。次の例のJavaコードはエンタープライズBean固有のコードですが、Java RMI-IIOP APIを使って作成されたサーバーにアクセスするCORBAクライアントの開発プロセスは共通です。
Java EEを実装するその他のベンダーが提供する類似アプリケーションへのリンクは、「同様のその他の例へのリンク」で確認できます。
次は、EJBコンポーネントにアクセスするCORBAクライアント・アプリケーションの開発例です。この例のクライアントはC++プログラミング言語で記述されていますが、実際には、CORBAによってサポートされている言語であればどの言語を使用してもかまいません。
次のセクションでは、エンタープライズBeanにアクセスできるCORBAクライアントの一般的な開発プロセスを紹介します。
例を単純化するため、一部の説明を省略しています。より高度なソリューションの構築方法については、「複雑なインタフェースの使用上のヒント」を参照してください。
次は、Java RMI-IIOPおよびCORBAクライアントからアプリケーション・サーバーに送信された単純なString
ログ・メッセージを受け付けるエンタープライズBeanのコード例です。このエンタープライズBeanは、受け付けたメッセージを現在のサーバー時間とともにサーバー上に出力します。
/Java/src/ejbinterop
ディレクトリに、Logger.java
、LoggerHome.java
、LoggerEJB.java
、LogMessage.java
の各ファイルを作成します。
Logger.java
ファイルは、エンタープライズBeanのリモート・インタフェースとして、EJBObject
オブジェクトを拡張します。リモート・インタフェースは、EJBオブジェクトのリモート・クライアント・ビューを提供し、リモート・クライアントから呼出し可能なビジネス・メソッドを定義します。
//Code Example 1: Logger.java package ejbinterop; import javax.ejb.EJBObject; import java.rmi.RemoteException; /** * Accepts simple String log messages and prints * them on the server. */ public interface Logger extends EJBObject { /** * Logs the given message on the server with * the current server time. */ void logString(String message) throws RemoteException; }
LoggerHome.java
ファイルは、EJBHome
を拡張します。EJBHome
インタフェースは、すべてのEJBコンポーネントのリモート・ホーム・インタフェースによって拡張される必要があります。ホーム・インタフェースは、リモート・クライアントによるEJBオブジェクトの作成、検索、削除を可能にするメソッドと、EJBインスタンスに固有ではないホーム・ビジネス・メソッドを定義します。
//Code Example 2: LoggerHome.java package ejbinterop; import java.rmi.RemoteException; import javax.ejb.EJBHome; import javax.ejb.CreateException; public interface LoggerHome extends EJBHome { Logger create() throws RemoteException, CreateException; }
LoggerEJB.java
ファイルには、セッションBeanのコードが格納されています。セッションBeanはクライアントによって作成されたエンタープライズBeanであり、通常、1回のクライアント・サーバー・セッションが終了するまで有効です。セッションBeanは、計算やデータベース・アクセスなどの処理を、クライアントに代わって実行します。この例のエンタープライズBeanは、クライアントから単純なString
ログ・メッセージを受け付け、サーバー上に出力します。
//LoggerEJB.java package ejbinterop; import javax.ejb.*; import java.util.*; import java.rmi.*; import java.io.*; /** * Accepts simple String log messages and prints * them on the server. */ public class LoggerEJB implements SessionBean { public LoggerEJB() {} public void ejbCreate() {} public void ejbRemove() {} public void ejbActivate() {} public void ejbPassivate() {} public void setSessionContext(SessionContext sc) {} /** * Logs the given message on the server with * the current server time. */ public void logString(String message) { LogMessage msg = new LogMessage(message); System.out.println(msg); } }
LogMessage.java
ファイルは、現在日時を記録し、メッセージを示す書式付きのString
を作成し、このメッセージをサーバーに出力します。
//LogMessage.java package ejbinterop; import java.io.Serializable; import java.util.Date; import java.text.*; /** * Simple message class that handles pretty * printing of log messages. */ public class LogMessage implements Serializable { private String message; private long datetime; /** * Constructor taking the message. This will * take the current date and time. */ public LogMessage(String msg) { message = msg; datetime = (new Date()).getTime(); } /** * Creates a formatted String showing the message. */ public String toString() { StringBuffer sbuf = new StringBuffer(); DateFormat dformat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG); FieldPosition fpos = new FieldPosition(DateFormat.DATE_FIELD); dformat.format(new Date(datetime), sbuf, fpos); sbuf.append(": "); sbuf.append(message); return sbuf.toString(); } }
javac -classpath $JAVA_EE_HOME/lib/java_ee.jar:.. *.java
これらのコマンドは、現在のディレクトリ内にすべての.java
ファイルのclass
ファイルを作成します。このコマンドを始め、このドキュメントで取り上げるコマンドを利用するためには、環境変数JAVA_EE_HOMEを正しく設定しておく必要があります。$JAVA_EE_HOMEを使用するのは、Unixオペレーティング環境の規約です。Microsoft Windowsオペレーティング環境で作業を行う場合は、この部分を%JAVA_EE_HOME%で置き換えてください。
このセクションでは、前のセクションで生成したJavaクラス・ファイルからインタフェース定義言語(IDL)ファイルを生成する方法を説明します。次の例では、JavaコードをIDLにマッピングする際、rmic
コンパイラを使用します。IDLでは、プログラミング言語に依存せずに、宣言するだけでオブジェクトのAPIを指定することができます。
rmic
コンパイラを実行します。
rmic -idl -noValueMethods -classpath $JAVA_EE_HOME/lib/java_ee.jar:<path_to_ejbinterop_dir> -d <path_to_where_idl_files_should_be_generated> ejbinterop.Logger ejbinterop.LoggerHome
この例では、javax.ejb
パッケージを定義するJARファイルとejbinterop
ファイルのディレクトリを取り込んでいます。Java Platform, Enterprise Edition (Java EE)リファレンス実装(RI)を使用する場合、JARファイルは$JAVA_EE_HOME/lib/java_ee.jar
にあります。
上の例のrmic
のコマンド行では、処理を高速化するため、noValueMethods
オプションを使用することをお薦めします。このオプションを指定すると、rmic
は、CORBA値型にマッピングされるパラメータまたは戻り値の型を持つメソッドをすべてスキップします。その結果、不要なIDLを生成してC++クライアントに実装することを防げます。ただし、この方法では、パラメータまたは戻り値として、プリミティブ・データ型、配列、およびStrings
しか使用できない(Javaクラス型を使用できない)という欠点もあります。詳細については、「複雑なインタフェースの使用上のヒント」を参照してください。
Javaクラス・ファイルに対してrmic
コンパイラを実行すると、上記のrmic
文の-dオプションで指定したディレクトリに、次のファイルが生成されます。
java/lang/Ex.idl
java/lang/Exception.idl
java/lang/Object.idl
java/lang/Throwable.idl
java/lang/ThrowableEx.idl
javax/ejb/CreateEx.idl
javax/ejb/CreateException.idl
javax/ejb/EJBHome.idl
javax/ejb/EJBMetaData.idl
javax/ejb/EJBObject.idl
javax/ejb/Handle.idl
javax/ejb/HomeHandle.idl
javax/ejb/RemoveEx.idl
javax/ejb/RemoveException.idl
ejbinterop/Logger.idl
ejbinterop/LoggerHome.idl
EJBMetaData
実装は、現在では各アプリケーション・サーバーに固有です。したがって、Javaプラットフォーム以外のプラットフォームで同等のものを開発し、長期間にわたって連続運用するのは困難です。これらをIDLから削除することもできますが、Javaインタフェースに変更を加えるたびに削除処理を実行し、再度rmic
コンパイラを使ってIDLファイルを生成する必要があるため、非常に面倒です。注: CORBA例外は継承をサポートしないため、Java言語とIDLのマッピングにより、実際のJava例外に相当するCORBA値型を含むEx
クラスが作成されます。ただし、ここで取り上げる例はごく基本的なものなので、例外のサポートまではあまり考慮しません。例外の詳細については、Exceptions
を参照してください。
クライアント・アプリケーションは、CORBAによってサポートされている任意の言語で記述できます。次に、Object Request Broker (ORB)とLoggerHome
のcorbaname
URLを持つ単純なC++クライアントのコードを紹介します。サーバー上の単純なString
メッセージを記録するコードです。include
文に変更を加え、C++ORBベンダーのライブラリに従って値ファクトリ登録コードを修正する必要があります。この例はORBacus for C++4.0.5用であり、一部はこの製品固有のC++コードです。
corbaname
URLは、ユーザーが読むことのできる形式のURLであり、CORBAオブジェクトへのアクセスを可能にします。これを使って、特定のネーミング・コンテキストから文字列化された名前を取得する(名前解決を行う)ことができます。これは、CORBA Interoperable Naming Service (INS)の一部です。INSは、Java EEプラットフォームの以前のリリースで配布されたCORBA Object Services (COS)ネーム・サービスの拡張です。INSの詳細は、「Interoperable Naming Service」を参照してください。
//Code Example: Client.cpp #include <fstream.h> // C++ ORB Vendor specific include files // These are from C++ ORBacus 4.0.5 #include <OB/CORBA.h> #include <OB/OBORB.h> // Include files generated from our IDL #include <java/lang/Exception.h> #include <java/lang/Throwable.h> #include <javax/ejb/CreateException.h> #include <javax/ejb/RemoveException.h> #include <ejbinterop/Logger.h> #include <ejbinterop/LoggerHome.h> /** * Given an ORB and a corbaname URL for a LoggerHome * object, logs a simple string message on the server. */ void run(CORBA::ORB_ptr orb, const char* logger_home_url) { cout << "Looking for: " << logger_home_url << endl; // Look up the LoggerHome object in the naming context // pointed to by the corbaname URL CORBA::Object_var home_obj = orb->string_to_object(logger_home_url); // Perform a safe downcast ejbinterop::LoggerHome_var home = ejbinterop::LoggerHome::_narrow(home_obj.in()); assert(!CORBA::is_nil(home)); // Create a Logger EJB reference ejbinterop::Logger_var logger = home->create(); CORBA::WStringValue_var msg = new CORBA::WStringValue((const CORBA::WChar*)L"Message from a C++ client"); cout << "Logging..." << endl; // Log our message logger->logString(msg); // Tell the application server we won't use this // EJB reference any more logger->remove(); cout << "Done" << endl; } /** * Simple main method that checks arguments, creates an * ORB, and handles exceptions. */ int main(int argc, char* argv[]) { int exit_code = 0; CORBA::ORB_var orb; try { // Check the arguments if (argc != 2) { cerr << "Usage: Client <corbaname URL of LoggerHome>" << endl; return 1; } // Create an ORB orb = CORBA::ORB_init(argc, argv); // Register value factories // NOTE: This is overkill for the example since we'll never // get these exceptions. Also, the _OB_id method is a // proprietary feature of ORBacus C++ generated code. CORBA::ValueFactory factory = new java::lang::Throwable_init; orb -> register_value_factory(java::lang::Throwable::_OB_id(), factory); factory -> _remove_ref(); factory = new java::lang::Exception_init; orb -> register_value_factory(java::lang::Exception::_OB_id(), factory); factory -> _remove_ref(); factory = new javax::ejb::CreateException_init; orb -> register_value_factory(javax::ejb::CreateException::_OB_id(), factory); factory -> _remove_ref(); factory = new javax::ejb::RemoveException_init; orb -> register_value_factory(javax::ejb::RemoveException::_OB_id(), factory); factory -> _remove_ref(); // Perform the work run(orb, argv[1]); } catch(const CORBA::Exception& ex) { // Handle any CORBA related exceptions cerr << ex._to_string() << endl; exit_code = 1; } // Release any ORB resources if (!CORBA::is_nil(orb)) { try { orb -> destroy(); } catch(const CORBA::Exception& ex) { cerr << ex._to_string() << endl; exit_code = 1; } } return exit_code; }
LoggerEJB
コンポーネントを配備する例です。
$JAVA_EE_HOME/bin/java_ee -verbose
$JAVA_EE_HOME/bin/deploytool
「ファイル」
->
「新規」
->
「アプリケーション」
を選択します。Logger.ear
と入力し、アプリケーションを作成するファイルを指定します。Logger
と入力します。「ファイル」
->
「新規」
->
「エンタープライズBean」
を選択します。ejbinterop
パッケージから4つの.class
ファイル(Logger.class
、LoggerHome.class
、LoggerEJB.class
、LogMessage.class
)を追加します。「了解」を選択し、「次」を選択します。「ステートレス
・セッション
Bean
タイプ」
を選択します。「エンタープライズ
Bean
クラス」
のejbinterop.LoggerEJB
を選択します。「リモート
・ホーム
・インタフェース」
のejbinterop.LoggerHome
を選択します。リモート
・インタフェース
」のejbinterop.Logger
を選択します。「セキュリティ
設定」
ページが表示されるまで、「次」ボタンを繰返し押します。「配備値の
設定」
ボタンを選択します。
「サポートする
クライアントの
選択」
を選択します。「ツール」
->
「配備」
を選択します。JNDI
名
」のLoggerEJBフィールドにejbinterop/logger
と入力します。これで、LoggerアプリケーションとLoggerEJB
コンポーネントが配備され、メッセージを受信する準備ができました。
Client corbaname:iiop:1.2@localhost:1050#ejbinterop/logger
Client
は実行するアプリケーションの名前。corbaname
は特定のネーミング・コンテキストから文字列化された名前を取得することを指定。iiop:1.2
はORBにIIOPプロトコルとGIOP 1.2を使用するように指示。localhost
。この例を応用して2台のマシンを実行する場合は、localhost
の位置にサーバーを実行するマシンのIPアドレスまたはホスト名を入力する。1050
はネーミング・サービスが要求を待機するポート。Java EE RIでは、ネーミング・サービスが待機するデフォルト・ポートは1050。参照部分からハッシュ・マークのあるポイント(Client corbaname:iiop:1.2@localhost:1050
)までが、ルート・ネーミング・コンテキストを返すURL。ejbinterop/logger
は解決するネーミング・コンテキスト内の名前。Java EEリファレンス実装を使用している場合は、アプリケーション・サーバー上に次のようなメッセージが表示されます。
Sep 21, 2001 3:33:07 PM PDT: Message from a C++ client ejbinterop/
logger
is the name to be resolved from the Naming Service.
$JAVA_EE_HOME/bin/java_ee -stop
実行プロセスの終了手順はオペレーティング・システムによって異なります。異なったサーバーを使用している場合は、システム文書で詳細を確認してください。
ここまでの例を使って、エンタープライズBeanに接続するJava RMI-IIOPクライアントを簡単に開発できます。次に、C++クライアントを使用する例との違いを挙げておきます。
クライアントJarを返します
」チェックボックスをオンにする。次は、LoggerEJB
コンポーネントのJava RMI-IIOPバージョンのクライアントのコードです。C++クライアントの例と同じ手順に従います。クライアントの実行時は、C++の例と同じURLを使用します。
//Code Example: LogClient.java package ejbinterop; import java.rmi.RemoteException; import javax.rmi.*; import java.io.*; import javax.naming.*; import javax.ejb.*; /** * Simple Java RMI-IIOP client that uses an EJB component. */ public class LogClient { /** * Given a corbaname URL for a LoggerHome, * log a simple String message on the server. */ public static void run(String loggerHomeURL) throws CreateException, RemoveException, RemoteException, NamingException { System.out.println("Looking for: " + loggerHomeURL); // Create an InitialContext. This will use the // CosNaming provider we will specify at runtime. InitialContext ic = new InitialContext(); // Lookup the LoggerHome in the naming context // pointed to by the corbaname URL Object homeObj = ic.lookup(loggerHomeURL); // Perform a safe downcast LoggerHome home = (LoggerHome)PortableRemoteObject.narrow(homeObj, LoggerHome.class); // Create a Logger EJB reference Logger logger = home.create(); System.out.println("Logging..."); // Log our message logger.logString("Message from a Java RMI-IIOP client"); // Tell the application server we won't use this // EJB reference anymore logger.remove(); System.out.println("Done"); } /** * Simple main method to check arguments and handle * exceptions. */ public static void main(String args[]) { try { if (args.length != 1) { System.out.println("Args: corbaname URL of LoggerHome"); System.exit(1); } LogClient.run(args[0]); } catch (Throwable t) { t.printStackTrace(); System.exit(1); } } }
C++クライアントではなくJava RMI-IIOPクライアントでアプリケーション例を実行するには、次の手順に従ってください。
ejbinterop
/ディレクトリ内の.java
ファイルをコンパイルします。
javac -classpath $JAVA_EE_HOME/lib/java_ee.jar:<ejbinterop_directory> *.java
java -classpath $JAVA_EE_HOME/lib/java_ee.jar: <path to LoggerClient.jar>/LoggerClient.jar: <directory_above_ejbinterop>:<ejbinterop_directory> ejbinterop.LogClient corbaname:iiop:1.2@localhost:1050#ejbinterop/logger
Java EE RIを実行しているウィンドウに、これが表示されます。
Jan 31, 2002 2:27:47 PM PST: Message from a Java RMI-IIOP client
クライアントを実行しているウィンドウには、次のメッセージが表示されます。
Looking for: corbaname:iiop:1.2@localhost:1050#ejbinterop/logger
Logging...
Done
このためには、rmic
の実行時に-noValueMethods
スイッチを省略します。さらに、IDLからC++言語へのマッピング・コンパイラを再実行し、生成された値型がサポートされるかどうかを確認します。
Logger
にLogMessage
をとるメソッドを追加。インタフェースは、それぞれ異なった言語を使用するクライアントとサーバー間の通信において重要な役割を果たします。次のヒントを参考にすることにより、この領域で成功する確率を高めることができます。
java.util
内のコレクションなど)を使用しない。これらの型をIDLにマッピングしたら、次にクライアント・プログラミング言語に実装する必要があります。Javaオブジェクトの直列化およびRMI-IIOP APIは、ワイヤー形式と、時間の経過とともに変化するクラスの内部表現を許可します。このため、CORBAクライアント・アプリケーションとJava Platform, Standard Edition (Java SE)の実装またはバージョンが非互換になる可能性があります。
戻り値の型またはメソッドのパラメータで複雑なデータ構造を使用しなければならない場合もあります。このような場合は、IDLで開始してみてください。データ構造と例外をIDLで定義してからEJBインタフェースで使用します。これにより、逆マッピングによってユーザーのCORBAインタフェースに侵入する不正行為を阻止できます。
たとえば、最初にIDLでLogMessage
クラスを定義しておき、Java言語からIDLへのコンパイルでは、Logger EJBコンポーネントのメソッド・パラメータとしてこのクラスを使用します。
CORBA IDLは、メソッドの多重定義をサポートしません。Java言語とIDLのマッピングでは、メソッド名をすべてのIDLパラメータ型と組み合わせるようなIDLメソッド定義を作成することによって、この問題を解決します。ただし、生成されるメソッド名は、Javaプログラミング言語以外の言語を使用する開発者にとってはたいへんわかりにくいものになります。
選択肢が非常にかぎられている場合や、記述しようとしているコードへの影響が大きい場合は、サーバー側ブリッジを利用してみてください。ブリッジの詳しい構築方法は、リンクのセクションに記載されているサイトで確認できます。
Java EEテクノロジを実装するベンダーの多くは、CORBAとEnterprise JavaBeansテクノロジを統合する優れた例とヒントを提供しています。たとえば、EJB-to-CORBA/Java Simpappサンプルアプリケーションを参照してください。