Session Beansは、クライアントが必要に応じて呼び出す処理をまとめたものです。クライアントの特定のセッションに対応しており、セッション開始と同時に生成され、セッション終了時に消滅します。
Session Beansにも、2つの種類があります。
ひとつめは、"Stateless Session Beans"です。Stateless Session Beansは、インスタンスに固有のフィールドがないため内部状態を持たず、ユーティリティクラスのように振る舞うEJBです。
ふたつめは、"Stateful Session Beans"です。こちらは内部状態を持っています。
ここでは、Stateless Session Beanについて解説しましょう。
本章では、Helloサンプルアプリケーションを作成します。ファイルの構成は次のとおりです。
まずは、Stateless Session Beansが公開するメソッドを定義しているインタフェースである"Hello.java"です。従来のEJBでは、EJBオブジェクトインタフェースを継承する必要がありましたが、EJB 3.0では、POJI + Annotationのスタイルとなっています。
import javax.ejb.Remote; @Remote public interface Hello { public String sayHello(String name); }
このインタフェースが、Remote (=別のマシン)で実行される可能性があるとします。この場合、インタフェースに@Remoteアノテーションを付加することで、このEJBの機能を別のマシンから呼びだすことができるようになります。具体的には、RMI/IIOPが用いられます。
@Remote public interface Hello {…}
同一のJava Virtual Machineからしか動かさない場合、@Localアノテーションを付加します。
@Local public interface Hello {…}
何もアノテーションを付けなかった場合、@Localを付けるのと同じことになります。
public interface Hello {…}
次は、Stateless Session Beanの本体です。先ほど作成したインタフェースを実装しています。
import javax.ejb.Stateless; @Stateless public class HelloImpl implements Hello { public String sayHello(String name) { return "Hello, " + name + "!"; } }
Stateless Session Beanであることを示すために、@Statelessアノテーションを付加しています。
従来のEJBでは、SessionBeanインタフェースを実装したり、ejbCreate, ejbRemoveメソッドなどを用意する必要がありました。EJB 3.0では、Helloインタフェースを実装したPOJOになっています。
続いて、クライアントプログラムです。クライアントプログラムでは、Helloインタフェースを呼び出す必要があります。どうやればよいのでしょうか。
import javax.naming.InitialContext; import javax.naming.Context; import javax.naming.NamingException; import javax.ejb.EJB; public class HelloClient { private static Hello hello; public static void main(String[] args) { try { Context context = new InitialContext(); hello = (Hello)context.lookup("Hello"); String message = hello.sayHello(args[0]); System.out.println(message); } catch (NamingException e) { e.printStackTrace(); } } }
このプログラムでは"JNDI"を使っています。"JNDI"は、Javaでディレクトリサービス(LDAPなど)にアクセスするためのAPIです。抽象的な「名前」をキーにしてオブジェクトを取得できます。JNDIはJDBC, EJB, JavaMailといった技術でよく利用されています。
ソースコードのうち、JNDIにアクセスする部分を抜き出してみましょう。
// Contextを取得 Context context = new InitialContext(); // 名前に対応する Hello を取得 hello = (Hello)context.lookup("Hello");
まず、InitialContextを取得しています。InitialContextとは、JNDIで検索をするときの出発点です。ファイルシステムに例えると、ルートディレクトリに相当します。Contextは、ディレクトリに相当します。
次に、Contextのlookupというメソッドを使い、"Hello"という名前からHello型のインスタンスを取得しています。
GlassFishでは次のコマンドを使って、どのような名前が有効になっているか調べることができます。
asadmin list-jndi-entries
では、今回のサンプルアプリケーションをGlassFishで動かすための手順をご紹介しましょう。
"JAVAEE_HOME"という環境変数を設定して、その値にGlassFishをインストールしたディレクトリを指定します。
そして、%JAVAEE_HOME%\binにPATHを通します。
GlassFishを起動します。
asadmin start-domain
サンプルアプリケーションをコンパイルします(Windowsで実行した例)。
cd hello-jndi javac -classpath %JAVAEE_HOME%\lib\javaee.jar *.java
Jarファイルを生成します。
jar cvf hello.jar Hello.class HelloImpl.class
そして、生成したJarファイルをGlassFishのコンテナ上に置きます。このことを「配備」あるいは「デプロイ」と呼びます。
copy hello.jar %JAVAEE_HOME%\domains\domain1\autodeploy
クライアントプログラムを実行します。
java -classpath %JAVAEE_HOME%\lib/javaee.jar; (つなげて入力) %JAVAEE_HOME%\lib\appserv-rt.jar;. HelloClient tomoharu
クライアントプログラムについて、もう少し考えてみましょう。
JNDIを使ったクライアントプログラムでは、「名前」をプログラムに直接記述することで、Helloオブジェクトを取得していました。
この「名前」は、コンテナ環境に依存しています。
このことを、Dependency Injectionを使って解決してみましょう。
import javax.ejb.EJB; public class HelloClient { @EJB static Hello hello; public static void main(String[] args) { String message = hello.sayHello(args[0]); System.out.println(message); } }
ここでは、helloフィールドに、"@EJB"というAnnotationが付加されています。このAnnotationは、該当するEJBを注入するためのものです。このことにより、コンテナ固有の情報がソースコードに表れなくなりました。
このプログラムでは、Dependency Injectionを用いるため、実行するには「クライアントコンテナ」というソフトウェアが必要になります。
Dependency Injectionを使ったクライアントプログラムの場合、appclientコマンドを使用する必要があります。
appclientコマンドは、クライアントコンテナを起動してアプリケーションを実行するためのコマンドです。このコマンドによって、@EJBへの依存性注入が行われます。
appclient HelloClient tomoharu