レベル: 中級 Kunal Mittal (kunal@kunalmittal.com), Portal/J2EE Architect, Consultant
2006年 1月 03日 Apache GeronimoにJSP (Java™Server Pages)、サーブレット、およびさまざまなEJB (Enterprise JavaBeans)をデプロイする方法を学びます。この記事では、他のJ2EE (Java 2 Platform, Enterprise Edition)コンテナーとは異なるApache Geronimoに必要なデプロイメント手順を説明します。
Apache Geronimoを実行しているコンピューターに簡単なJ2EEアプリケーションをデプロイする手順を説明し、他のJ2EE準拠のアプリケーション・サーバー用の手順とどの程度似ているかを調べます。この記事ではApache GeronimoにEJBをデプロイする方法を中心に解説しますが、JSPとサーブレットについても学びます。この記事では、読者がIBM WebSphere® Application Server、JBoss、WebLogicなど、別のJ2EEコンテナーでEJBを作成しデプロイする方法をご存じであることを前提としています。
注: この記事のコード・サンプルを使うときは、Apache Geronimoの現リリース(執筆時点でVersion 1.0 M5)を使ってください。
この記事の例にあるすべてのコードは、Apache Mavenを使ってビルドし、デプロイしています。したがって、多くのファイルはMaven固有のビルド・スクリプトです。出力はEAR(Enterprise Archive)ファイルです。.earファイルがビルドされた後、次のコマンドを実行してApache Geronimoにデプロイする必要があります。
 |
J2EE のデプロイメント・プロセス
この記事の例にあるすべてのコードは、Apache Mavenを使ってビルドし、デプロイしています。したがって、多くのファイルはMaven固有のビルド・スクリプトです。出力はEAR(Enterprise Archive)ファイルです。.earファイルがビルドされた後、次のコマンドを実行してApache Geronimoにデプロイする必要があります。
$ java -jar bin/deployer.jar deploy phonebook.ear
|
|
デプロイメント・プロセスを説明する土台として「Dive into EJB Web applications with Geronimo」(developerWorks、2005年7月)からコードを使うことを許可してくれたNeal Sancheに感謝します。コードをダウンロードし、この記事を読みながら参照してください(「ダウンロード」セクションを参照)。
JSPとサーブレット
JSPとサーブレットは、J2EEアプリケーションのUI (ユーザー・インターフェース)層を動かす2つの基本的なJ2EE技術です。JSPは主にプレゼンテーション・ロジックとHTMLコードに使います。サーブレットは通常のMVC (Model-View-Controller)アーキテクチャーのコントローラー層を形成し、プレゼンテーション層とモデル層の間のインターフェースとしての役割を果たします。
コード例の簡単なアプリケーションはApache Strutsを使って書かれています。コードには、いくつかのStrutsアクション・クラスとJSPが入っています。図1は、ソース・コードの構造を示しています。
図1. サンプルのソース・コードの構造
Strutsアクション・クラスはPhonebook/src/java/org/acme/phonebook/strutsディレクトリにあります。JSPはPhonebook/src/webapp/pagesディレクトリにあります。
この例で唯一実際のサーブレットはStruts Action Servletです。これは、アクション・クラスの呼び出しを制御します。コード・ツリーでservlet-mappings.xmlとservlets.xmlを探し、Struts Action Servletがどのように宣言されているか調べてください。リスト1とリスト2に、それぞれのファイルのコードを示します。
リスト1.servlet-mappings.xml
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Session Config -->
<session-config>
<!-- Make sessions last two hours -->
<session-timeout>120</session-timeout>
</session-config>
|
リスト2.servlets.xml
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/conf/struts-config.xml
</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>0</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet> |
J2EE Webプログラマにとって、このコードはおなじみです。servlet-mappings.xmlとservlets.xmlファイルは、Mavenのビルド・プロセス中にJ2EE WAR (Web Archive)ファイルのweb.xmlファイルにマージされます。ビルド・プロセスを見直し、生成されたweb.xmlファイルを調べることをお勧めします。
EJBs
Apache GeronimoではEJBコンテナー・システムとしてOpenEJBを使います。例には2つのEJBが含まれています。
- コンテナー管理エンティティーEJB
- ステートレス・セッションEJB
コンテナー管理エンティティーEJB
Nealはその記事の中で、EJBでXDocletを大変活用したと説明しています。XDocletは、デプロイメント記述子を含め、EJBGenと同じようにほとんどのEJBコードを自動的に生成します。XDocletの考え方はJavaアノテーションに似ています。JavadocスタイルのコメントがXDocletコンパイラのトリガとして使われ、必要な多くのコードが生成されます。リスト3に示すPhoneBookEntryBean.javaで、XDocletで使われるアノテーション・スタイルを確認してください。
リスト3.PhoneBookEntryBean.java
package org.acme.phonebook.ejb;
/**
*
* @ejb.bean
* type="CMP"
* cmp-version="2.x"
* name="PhoneBookEntry"
* local-jndi-name=
* "org.acme.phonebook.ejb/PhoneBookEntryLocalHome"
* view-type="local"
* primkey-field="name"
*
* @ejb.finder
* signature="java.util.Collection findAll()"
* query="SELECT OBJECT(o) from PhoneBookEntry AS o"
*
* @xx-ejb.data-object
* container="true"
* setdata="true"
* generate="true"
*
* @ejb.value-object
*
* @ejb.transaction type="Required"
* @ejb.permission unchecked="true"
* @struts.form include-all="true"
*
* @web.ejb-local-ref
* name="ejb/PhoneBookEntryLocal"
* type="Entity"
* home="org.acme.phonebook.ejb.PhoneBookEntryLocalHome"
* local="org.acme.phonebook.ejb.PhoneBookEntryLocal"
* link="PhoneBookEntry"
*
* @ejb.persistence table-name="PhoneBookEntry"
*
*/
public abstract class PhoneBookEntryBean
implements javax.ejb.EntityBean
{
/**
*
* @ejb.pk-field
* @ejb.persistence
* column-name="NAME"
* jdbc-type="VARCHAR"
* sql-type="VARCHAR(250)"
*
* @ejb.interface-method view-type="local"
*
*/
public abstract java.lang.String getName();
/**
* @ejb.interface-method view-type="local"
*/
public abstract void setName(java.lang.String newValue);
/**
*
* @ejb.persistence
* column-name="PHONE_NUMBER"
* jdbc-type="VARCHAR"
* sql-type="VARCHAR(250)"
*
* @ejb.interface-method view-type="local"
*
*/
public abstract java.lang.String getPhoneNumber();
/**
* @ejb.interface-method view-type="local"
*/
public abstract void setPhoneNumber(java.lang.String newValue);
/**
* @ejb.interface-method
*/
public abstract org.acme.phonebook.ejb.PhoneBookEntryValue
getPhoneBookEntryValue();
/**
* @ejb.create-method
*/
public java.lang.String ejbCreate(java.lang.String name,
java.lang.String phoneNumber)
throws javax.ejb.CreateException
{
setPhoneNumber(phoneNumber);
setName(name);
return null; // should not return primaryKey for CMP:
}
public void ejbPostCreate (java.lang.String name,
java.lang.String phoneNumber)
throws javax.ejb.CreateException
{
}
/**
* This is a create method which takes only the value of the
* primary key, because this object does not have automatic
* key generation turned on.
*
* @ejb.create-method
*/
public java.lang.String ejbCreate(java.lang.String name) throws
javax.ejb.CreateException {
setName(name);
return null;
}
public void ejbPostCreate(java.lang.String name)
throws javax.ejb.CreateException {
}
}
|
リスト3に示したPhoneBookEntryBean. javaで、Javadocコメントをよくご覧ください。このコードでは、コード自体よりJavadocコメントの方が興味深く、情報量も豊富です。このアノテーションでは、EJBの種類、コンテナー管理フィールド、ファインダー・クエリー、データ型など、EJBのデプロイメント記述子に通常指定されるすべての情報が定義されています。さらに、ホーム・インターフェース・クラス、リモート・インターフェース・クラス、およびローカル・インターフェイス・クラスをコーディングする必要はありません。MavenとXDocletにより、ビルド・プロセス中にバックグラウンドで自動的に行われます。
OpenEJBコンテナー・システムを使うときに必要な基本デプロイメント記述子は、リスト4に示すopenejb-jar.xmlです。この記述子はPhoneBookEntryBean.javaのJavadocアノテーションと組み合わされて、最終的に、J2EE準拠の標準的EJBアプリケーションに必要なejb-jar.xmlとその他の記述子を形成します。
リスト4.openejb-jar.xml
<?xml version="1.0"?>
<openejb-jar
xmlns="http://www.openejb.org/xml/ns/openejb-jar"
configId="org/acme/PhonebookEJB"
parentId="MysqlDatabase">
<cmp-connection-factory>
<resource-link>MysqlDataSource</resource-link>
</cmp-connection-factory>
<enterprise-beans>
<entity>
<ejb-name>PhoneBookEntry</ejb-name>
<jndi-name>PhoneBookEntry</jndi-name>
<local-jndi-name>
java:comp/env/ejb/PhoneBookEntryLocal
</local-jndi-name>
<table-name>phone</table-name>
<cmp-field-mapping>
<cmp-field-name>name</cmp-field-name>
<table-column>name</table-column>
</cmp-field-mapping>
<cmp-field-mapping>
<cmp-field-name>phoneNumber</cmp-field-name>
<table-column>phone</table-column>
</cmp-field-mapping>
</entity>
<session>
<ejb-name>PhoneBookSession</ejb-name>
<jndi-name>
org.acme.phonebook.ejb/PhoneBookSession/Home
</jndi-name>
<local-jndi-name>
java:comp/env/ejb/PhoneBookSessionLocal
</local-jndi-name>
</session>
</enterprise-beans>
</openejb-jar>
|
注: Aaron Mulder著の「Apache Geronimo Development and Deployment」には、このXMLファイルの構造が視覚的にわかりやすく表現されています(この本へのリンクについては「参考文献」を参照)。
- このファイルでは、基本的に次のことを行います。
- コンテナー内にデプロイされるエンティティーBeanを定義します。
- JNDI (Java Naming and Directory Interface)名を設定します。
- BeanがエンティティーEJBであることを宣言します。
- 使用するデータ・ソースを定義します。
- このエンティティーEJBが表すテーブルのテーブル名を定義します。
- コンテナー管理フィールドを、それが表すデータベース・テーブルの該当する列で定義します。
以上で終わりです。エンティティーEJBのデプロイメントに必要なものは、簡単なJavaクラス1つとこのデプロイメント記述子だけです。以前は、エンティティーEJBの作成に少なくとも1、2日かかりました。今では、2、3時間しかかかりません。
ステートレス・セッションEJB
セッションEJBは、デプロイメントに関しては、基本的にエンティティーEJBと同じように動作します。実際、ここでのXDocletの使い方は、前に説明したエンティティーEJBの場合と似ています。リスト5に示すPhoneBookSessionBean.javaは、セッションEJBの使用に必要なデプロイメント記述子の基本を示しています。実際には、エンティティーEJBのデプロイメント記述子セクションよりずっと簡単です。
リスト5.PhoneBookSessionBeasn.java
package org.acme.phonebook.ejb;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Phone book session bean. A place for all of the useful business
* methods that can be done with phone book entries.
*
* @ejb.bean name="PhoneBookSession"
* type="Stateless"
* local-jndi-name="java:comp/env/ejb/PhoneBookSessionLocal"
* jndi-name="org.acme.phonebook.ejb/PhoneBookSession/Home"
* view-type="both"
*
* @ejb.permission unchecked="true"
*
* @ejb.interface generate="local,remote"
* remote-class="org.acme.phonebook.ejb.PhoneBookSession"
* local-class="org.acme.phonebook.ejb.PhoneBookSessionLocal"
* @ejb.home generate="local, remote"
* remote-class="org.acme.phonebook.ejb.PhoneBookSessionHome"
* local-class="
* org.acme.phonebook.ejb.PhoneBookSessionLocalHome"
* @ejb.util generate="physical"
* @ejb.ejb-ref ejb-name="PhoneBookEntry" view-type="local"
* ref-name="ejb/PhoneBookEntryLocal"
* @web.ejb-local-ref
* name="ejb/PhoneBookSessionLocal"
* type="Session"
* home="org.acme.phonebook.ejb.PhoneBookSessionLocalHome"
* local="org.acme.phonebook.ejb.PhoneBookSessionLocal"
* link="PhoneBookSession"
*
*/
public abstract class PhoneBookSessionBean implements
javax.ejb.SessionBean {
/**
* Add a phone book entry.
* @param name the name
* @param number the number
*
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public void addEntry(java.lang.String name,
java.lang.String number) {
try {
PhoneBookEntryLocal entry =
PhoneBookEntryUtil.getLocalHome().
create(name,number);
} catch(Throwable ex) {
ex.printStackTrace();
}
}
/**
* Delete a phone book entry for the given name
* @param name the name to remove
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public void deleteEntry(java.lang.String name) {
try {
PhoneBookEntryLocal entry =
PhoneBookEntryUtil.getLocalHome().
findByPrimaryKey(name);
entry.remove();
} catch (Throwable ex) {
ex.printStackTrace();
}
}
/**
* Update a phone book entry for the given
* name with the given number.
* @param name the name
* @param number the phone number
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public void updateEntry(java.lang.String name,
java.lang.String number) {
try {
PhoneBookEntryLocal entry =
PhoneBookEntryUtil.getLocalHome().
findByPrimaryKey(name);
entry.setPhoneNumber(number);
} catch (Throwable ex) {
ex.printStackTrace();
}
}
/**
* Find an entry by name.
* @param name the name to look up.
* @return a phone book entry value representing the entry.
*
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public PhoneBookEntryValue findEntry(java.lang.String name) {
try {
PhoneBookEntryLocal entry =
PhoneBookEntryUtil.getLocalHome().
findByPrimaryKey(name);
return entry.getPhoneBookEntryValue();
} catch (Throwable ex) {
ex.printStackTrace();
}
return null;
}
/**
* List all of the phone book entries.
* @return a collection of PhoneBookEntryValue objects.
*
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public java.util.Collection listEntries() {
ArrayList values = new ArrayList();
try {
Collection entries =
PhoneBookEntryUtil.getLocalHome().findAll();
Iterator i = entries.iterator();
while(i.hasNext()) {
PhoneBookEntryLocal entry =
(PhoneBookEntryLocal)i.next();
values.add(entry.getPhoneBookEntryValue());
}
} catch (Throwable ex) {
ex.printStackTrace();
}
return values;
}
/**
* @ejb.create-method
* @ejb.transaction type="Required"
*/
public void ejbCreate ()
throws javax.ejb.CreateException
{
}
public void ejbPostCreate ()
throws javax.ejb.CreateException
{
}
protected javax.ejb.SessionContext _ctx = null;
public void setSessionContext(javax.ejb.SessionContext ctx)
{
_ctx = ctx;
}
protected javax.ejb.SessionContext getSessionContext()
{
return _ctx;
}
} |
実際のところ、PhoneBookSessionBean.javaでは、JNDI名、メソッドのトランザクション属性、セッションEJBのホーム・インターフェース、リモート・インターフェースなどの名前しか定義していません。セッションEJBのJ2EEデプロイメント記述子に通常見られるその他の設定は、セッションEJBのJavaソース・コードに定義されたアノテーションを使って生成されます。その中には、各メソッドのトランザクション・タイプ、EJBがステートレスEJBであることなども含まれます。
XDocletでは、ホーム・インターフェースとリモート・インターフェースの検索に使われるユーティリティー・クラスも生成します。さまざまなアプリケーションのEJBを作成する開発者はその利点を実感するでしょう。この検索コードの作成はやりにくく、正直のところ退屈です。開発者は常に、以前行った仕事を調べたり、Webを検索したりして、このコードを作業中の新しいアプリケーションにカット・アンド・ペーストします。XDocletでは、自動的に生成されるようになりました。
最後の手順
前に説明したようにEJBをコーディングし、デプロイメント記述子を生成した後、Mavenを実行してコードをビルドする前にもう少し作業が必要です。リスト6は、Apache Geronimoに必要なデプロイメント記述子application.xmlを示しています。これは、J2EE 1.4に標準的に必要となるデプロイメント記述子です。application.xmlでは、JAR (Java Archive)ファイル、WARファイル、およびこのJ2EE EARの一部として必要なその他の外部リソースを定義します。
リスト6.application.xml
<application
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
version="1.4">
<module>
<ejb>phonebook-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>phonebook.war</web-uri>
<context-root>/phonebook</context-root>
</web>
</module>
<module>
<connector>
tranql-connector-1.0-SNAPSHOT.rar</connector>
</module>
</application>
|
以上すべてのXMLファイルの準備ができたら、例に用意されているMavenビルド・スクリプトを実行して、すべてのソース・コードをすばやくビルドできます。ビルド・プロセスでは、XDocletライブラリを使ってコードが生成されます。Mavenの設定には注意が必要です。Mavenの準備と実行を行うには、記事「Dive into EJB Web applications with Geronimo」の手順に従ってください。
結論
この記事では、Apache Geronimoを使うJ2EEアプリケーションのデプロイメントに必要なさまざまなデプロイメント記述子を生成する方法を説明しました。いかに簡単にEJBをコーディングし、Apache Geronimoを実行しているコンピューターにデプロイできるかを示すことで、XDocletとMavenの能力を表しました。
この記事から明らかなことは、Apache Geronimoを実行しているコンピューターにEJB をデプロイする手順は、実際にはJBoss、WebSphere、WebLogicなどの他のJ2EE準拠アプリケーション・サーバーの場合と変わらないことです。他のアプリケーション・サーバーを使う場合でも、MavenとXDocletを使用できます。
ダウンロード
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Kunal Mittalは、Java技術、J2EE、およびWebサービス技術を専門とするコンサルタントです。こうした分野の書籍の共著や寄稿を行っています。現在、Sony Pictures Entertainmentのポータル・プロジェクトに携わっています。詳細については、著者のWebサイトhttp://www.soaconsultant.comを参照してください。 |
記事の評価
|