このドキュメントは、CORBA によってサポートされている任意の言語で記述されたクライアントから Enterprise JavaBeansTM コンポーネント (以下「EJBTM コンポーネント」) にアクセスできるようにする方法を説明します。このドキュメントは、JavaTM 2 Platform, Enterprise Edition (以下「J2EETM」) および CORBA (Common Object Request Broker Architecture) に関する高度な知識をお持ちのプログラマを対象に書かれています。
J2EE テクノロジは、エンタープライズアプリケーションを単純化します。J2EE テクノロジにより、エンタープライズアプリケーションは、Enterprise JavaBeansTM (EJBTM) に基づいたモジュール型の再利用可能な標準コンポーネントに準拠するようになり、J2EE テクノロジから提供された完全なサービスセットを利用できるようになります。アプリケーションの細かい動作の多くは、J2EE テクノロジによって自動的に処理されます。時間のかかる複雑なアプリケーション開発タスクが自動化されるため、エンタープライズ開発者は、インフラストラクチャーの構築よりもビジネスロジックの拡張に力を注ぐことができます。
EJBTM のサーバー側コンポーネントモデルを使うと、トランザクションに適した、拡張性と移植性の高いミドルウェアコンポーネントを簡単に開発できます。Enterprise JavaBeans サーバーを使用することで、トランザクション、セキュリティー、データベース接続性などのミドルウェアサービスが自動的にサポートされるため、ミドルウェア開発の複雑さが軽減されます。
CORBA は、オブジェクト管理グループ (OMG) の規格の 1 つです。ベンダーに依存しないオープンなアーキテクチャーと、複数のコンピュータアプリケーションがネットワーク経由で利用できるインフラストラクチャーという特徴を備えています。CORBA ベースのプログラムは、標準プロトコルである IIOP (Internet Inter-ORB Protocol) を使って、ほかの CORBA ベースのプログラムと連携できます。このとき、双方のプログラムの作成元ベンダーは同じでも異なっていてもかまいません。これらのプログラムが利用するコンピュータ、オペレーティングシステム、プログラミング言語、ネットワークについても、ほとんど制限はありません。CORBA の詳細については、http://www.omg.org/gettingstarted/gettingstartedindex.htm を参照してください。
CORBA テクノロジは分散オブジェクトフレームワーク、そのフレームワークをサポートするサービス、および他言語との相互運用性を提供して Java プラットフォームを補完します。CORBA テクノロジは、Java 2 プラットフォームの重要な部分であり、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 の詳細については、http://www.omg.org/gettingstarted/omg_idl.htm を参照してください。
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 クライアントの開発プロセスは共通です。
J2EE テクノロジを実装するその他のベンダーが提供する類似アプリケーションへのリンクは、「同様のその他の例へのリンク」で確認できます。
次は、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 $J2EE_HOME/lib/j2ee.jar:.. *.java
これらのコマンドは、現在のディレクトリ内にすべての .java ファイルの class ファイルを作成します。このコマンドを始め、このドキュメントで取り上げるコマンドを利用するためには、環境変数 J2EE_HOME を正しく設定しておく必要があります。$J2EE_HOME を使用するのは、Unix® オペレーティング環境の規約です。Microsoft Windows オペレーティング環境で作業を行う場合は、この部分を %J2EE_HOME% で置き換えてください。
このセクションでは、前のセクションで生成した Java クラスファイルからインタフェース定義言語 (IDL) ファイルを生成する方法を説明します。次の例では、Java コードを IDL にマッピングする際、rmic コンパイラを使用します。IDL では、プログラミング言語に依存せずに、宣言するだけでオブジェクトの API を指定することができます。
rmic コンパイラを実行します。
rmic -idl -noValueMethods -classpath $J2EE_HOME/lib/j2ee.jar:<path_to_ejbinterop_dir> -d <path_to_where_idl_files_should_be_generated> ejbinterop.Logger ejbinterop.LoggerHome
この例では、javax.ejb パッケージを定義する JAR ファイルと ejbinterop ファイルのディレクトリを取り込んでいます。JavaTM 2 Platform, Enterprise Edition (J2EETM) version 1.3 リファレンス実装 (RI) を使用する場合、JAR ファイルは $J2EE_HOME/lib/j2ee.jar にあります。
上の例の rmic のコマンド行では、処理を高速化するため、noValueMethods オプションを使用することをお勧めします。このオプションを指定すると、rmic は、CORBA 値型にマッピングされるパラメータまたは戻り値の型を持つメソッドをすべてスキップします。その結果、不要な IDL を生成して C++ クライアントに実装することを防げます。ただし、この方法では、パラメータまたは戻り値として、プリミティブデータ型、配列、および Strings しか使用できない (Java クラス型を使用できない) という欠点もあります。詳細については、「複雑なインタフェースの使用上のヒント」を参照してください。
Java クラスファイルに対して rmic コンパイラを実行すると、上記の rmic 文の -d オプションで指定したディレクトリに、次のファイルが生成されます。
java/lang/Ex.idljava/lang/Exception.idljava/lang/Object.idljava/lang/Throwable.idljava/lang/ThrowableEx.idljavax/ejb/CreateEx.idljavax/ejb/CreateException.idljavax/ejb/EJBHome.idljavax/ejb/EJBMetaData.idljavax/ejb/EJBObject.idljavax/ejb/Handle.idljavax/ejb/HomeHandle.idljavax/ejb/RemoveEx.idljavax/ejb/RemoveException.idlejbinterop/Logger.idlejbinterop/LoggerHome.idlEJBMetaData 実装は、現在では各アプリケーションサーバーに固有です。したがって、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 は、J2EE プラットフォームの以前のリリースで配布された 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 コンポーネントを配備する例です。
$J2EE_HOME/bin/j2ee -verbose
$J2EE_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 はネーミングサービスが要求を待機するポート。J2EE バージョン 1.3 RI では、ネーミングサービスが待機するデフォルトポートは 1050。参照部分からハッシュマークのあるポイント (Client corbaname:iiop:1.2@localhost:1050) までが、ルートネーミングコンテキストを返す URL。ejbinterop/logger は解決するネーミングコンテキスト内の名前。J2EE 1.3 リファレンス実装を使用している場合は、アプリケーションサーバー上に次のようなメッセージが表示されます。
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.
$J2EE_HOME/bin/j2ee -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 $J2EE_HOME/lib/j2ee.jar:<ejbinterop_directory> *.java
java -classpath $J2EE_HOME/lib/j2ee.jar: <path to LoggerClient.jar>/LoggerClient.jar: <directory_above_ejbinterop>:<ejbinterop_directory> ejbinterop.LogClient corbaname:iiop:1.2@localhost:1050#ejbinterop/logger
J2EE 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 クライアントアプリケーションと JavaTM 2 Platform, Standard Edition (J2SETM) の実装またはバージョンが非互換になる可能性があります。
戻り値の型またはメソッドのパラメータで複雑なデータ構造を使用しなければならない場合もあります。このような場合は、IDL で開始してみてください。データ構造と例外を IDL で定義してから EJB インタフェースで使用します。これにより、逆マッピングによってユーザーの CORBA インタフェースに侵入する不正行為を阻止できます。
たとえば、最初に IDL で LogMessage クラスを定義しておき、Java 言語から IDL へのコンパイルでは、Logger EJB コンポーネントのメソッドパラメータとしてこのクラスを使用します。
CORBA IDL は、メソッドの多重定義をサポートしません。Java 言語と IDL のマッピングでは、メソッド名をすべての IDL パラメータ型と組み合わせるような IDL メソッド定義を作成することによって、この問題を解決します。ただし、生成されるメソッド名は、Java プログラミング言語以外の言語を使用する開発者にとってはたいへんわかりにくいものになります。
選択肢が非常にかぎられている場合や、記述しようとしているコードへの影響が大きい場合は、サーバー側ブリッジを利用してみてください。ブリッジの詳しい構築方法は、リンクのセクションに記載されているサイトで確認できます。
J2EE テクノロジを実装するベンダーの多くは、CORBA と Enterprise JavaBeans テクノロジを統合する優れた例とヒントを提供しています。