「技評のJBoss入門」の編集履歴(バックアップ)一覧はこちら
「技評のJBoss入門」(2006/03/20 (月) 01:47:23) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
* 技評の JBoss 入門
JBoss site のマニュアルから入るのがよいのかもしれないが、
ざっと全体を眺めるため、日本語の入門本で動作を確認してみる。
ベースが 3.0.7/3.2.0 なので 4.0.3 SP1 で動くか少し心配。
細かいエラーを覚えてないが、4.0.3 でなかなか消えないエラーが SP1 にしたことで
あっさり消えたので、今回も SP1 を使用した。
** 1章
JBoss モジュールの紹介や、EJB の基礎
*** EJB の種類
いまさらですが...
- Session Bean
-- javax.ejb.SessionBean
- Entity Bean
-- javax.ejb.EntityBean
- Message Driven Bean
*** 必須インターフェイス
- Component Interface
- Home Interface
- Bean class
- Deployment Descriptor ejb-jar.xml
*** EJB-JAR file structure
xxejb.jar
+- Sample.class # component interface
+- SampleHome.class # home interface
+- SampleBean.class # EJB Bean
+- META-INF
+- ejb-jar.xml
ejb-jar のサンプルは #step1 内にある
*** EJB Interface: Remote and Local
| | Component Interface | Home Interface |
| Remote Interface | javax.ejb.EJBObject | javax.ejb.EJBHome |
| Local Interface | javax.ejb.EJBLocalObject | javax.ejb.EJBLocalHome |
*** JBoss deployment descriptor
- jboss.xml
-- JNDI name 等 ejb-jar.xml にかけない設定情報を記述する. WEB-INF におく
- jboss-web.xml
-- JNDI name 等 web.xml に記述できない設定情報を記述
- jbosscmp-jdbx.cml
-- CMP Entity EJB module 用。データソース名、フィールドと SQL タイプの
マッピングなど
** 2章 プログラミングの基礎
*** step1
最初のサンプル。簡単な CMP Entity Bean を jar + META-INF/ejb-jar.xml
形式でデプロイする。クライアントサイドはスタンドアローンプログラム。
: Counter | Component interface, extends EJBObject, getValue(), setValue()
などを定義するインターフェイス
: CounterHome | Home interface, extends EJBHome, create(), findByPrimaryKey(),
findAll() 等を定義するインターフェイス
: CounterBean | Entity Bean, public abstract class CounterBean implements EntityBean,
永続化フィールドに関する setter, getter を abstract で定義. ''ejbCreate''
をはじめ、ejbActivate(), ejbPassivate(), ejbLoad(), ejbStore, ejbRemove()
等が定義される。
: CounterClient | JNDI で Home インターフェイスを取得し、プライマリキーで
エンティティビーンをみつけて、なければ作成。findAll() ですべてのエンティティ
を見つけて表示する。
: step1-ejb.jar | jar file, web.xml は入っていない。純粋に EJB のみを含む。
META-INF/
META-INF/MANIFEST.MF
sample/
sample/ejb/
META-INF/ejb-jar.xml
META-INF/jboss.xml
META-INF/jbosscmp-jdbc.xml
sample/ejb/Counter.class
sample/ejb/CounterBean.class
sample/ejb/CounterHome.class
deploy 時のメッセージ
17:36:35,948 INFO [ProxyFactory] Bound EJB Home 'Counter' to jndi 'ejb/step1/co
unter'
17:36:36,620 INFO [EJBDeployer] Deployed: file:/C:/usr/local/jboss/server/defau
lt/deploy/step1-ejb.jar
: ejb-jar.xml | Entity Bean の定義
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<home>sample.ejb.CounterHome</home>
<remote>sample.ejb.Counter</remote>
<ejb-class>sample.ejb.CounterBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<abstract-schema-name>Counter</abstract-schema-name>
<cmp-field><field-name>name</field-name></cmp-field>
<cmp-field><field-name>value</field-name></cmp-field>
<primkey-field>name</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>
:jboss.xml | EJB と JNDI の対応付け
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC
"-//JBoss//DTD JBOSS//EN"
"http://www.jboss.org/j2ee/dtd/jboss.dtd">
<jboss>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<jndi-name>ejb/step1/counter</jndi-name>
</entity>
</enterprise-beans>
</jboss>
:jbosscmp-jdbc.xml|CMP bean を DB にどのようにおくかを指定
<?xml version="1.0"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC
"-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN"
"http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">
<jbosscmp-jdbc>
<defaults>
<create-table>true</create-table>
<remove-table>true</remove-table>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<table-name>step1</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
:jndi.properties | 以下の内容の物でコンパイルの過程でクラスのディレクトリ
にコピーされているが jar には含まれていない。動いてしまうのはこれらが
デフォルトと一致しているためか... いや、違う、これはクライアントプログラム
のための物だ。
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
実行してみるとエラーになった。
E:\ghjbossbook\samples\chap2\step1>step1
[test]
javax.naming.CommunicationException [Root exception is java.io.InvalidClassException: org.jboss.invocation.InvokerInterceptor; unable to create instance]
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:707)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:572)
at javax.naming.InitialContext.lookup(Unknown Source)
at sample.client.CounterClient.getHome(CounterClient.java:48)
at sample.client.CounterClient.main(CounterClient.java:19)
Caused by: java.io.InvalidClassException: org.jboss.invocation.InvokerInterceptor; unable to create instance
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.ClientContainer.readExternal(ClientContainer.java:142)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at java.rmi.MarshalledObject.get(Unknown Source)
at org.jnp.interfaces.MarshalledValuePair.get(MarshalledValuePair.java:57)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:637)
... 4 more
ふと思い出したが、client の jar ファイルの構成が変わっていたためにトラブルが
あったような気がした。確かに実行ファイルにしても実行用の ant task にしても
クラスパスに通す jar ファイルはハードコーディングしてある。
次の様なタスクを加えて実行すると正しく動作した。
<target name="runclient">
<java classname="sample.client.CounterClient" fork="yes" dir="${build.classes}">
<classpath>
<fileset dir="${jboss.home}/client">
<include name="**/*.jar"/>
</fileset>
<pathelement location="${build.classes}"/>
</classpath>
</java>
</target>
runclient:
[java] log4j:WARN No appenders could be found for logger (org.jboss.security.SecurityAssociation).
[java] log4j:WARN Please initialize the log4j system properly.
[java] name=counter,value=2
直ったけれど、率直な感想としてもっとわかりやすいエラーにしてほしい。
ネストした例外をすべて出せるかどうかわからないが、jar ファイルが足りず
エラーになるということは ClassNotFoundException が大本ではないの?
とするとそのクラス名から jar ファイルが足りないという線は直ぐにでて
きそうな物だが...
*** Step 2
Web コンテナから実行する方法の解説
: war file | 次の様な構成をとる
xxxweb.war # .war で終わればよい
+- WEB-INF
| +- web.xml
+- classes/ # class files
+- lib/ # jar files
+- <HTML, JSP, image file 等>
: web.xml | ServletContext init param, session config, servlet/jsp name
ビルド前の構成
│ build.xml
│
├─build
│ ├─classes
│ │ │ jndi.properties
│ │ │
│ │ ├─META-INF
│ │ │ ejb-jar.xml
│ │ │ jboss.xml
│ │ │ jbosscmp-jdbc.xml
│ │ │
│ │ └─sample
│ │ ├─ejb
│ │ │ Counter.class
│ │ │ CounterBean.class
│ │ │ CounterHome.class
│ │ │
│ │ ├─util
│ │ │ CounterUtil.class
│ │ │
│ │ └─web
│ │ CounterServlet.class
│ │
│ └─lib
│ sample-ejb-client.jar
│ step2-ejb.jar
│ step2.war
│
├─lib
│ javax.servlet.jar
│
├─resources
│ jndi.properties
│
└─src
└─sample
├─ejb
│ │ Counter.java
│ │ CounterBean.java
│ │ CounterHome.java
│ │
│ └─META-INF
│ ejb-jar.xml
│ jboss.xml
│ jbosscmp-jdbc.xml
│
├─util
│ CounterUtil.java
│
└─web
│ CounterServlet.java
│ index.jsp
│ ShowCounter.jsp
│
└─WEB-INF
web.xml
ant で build すると step1 相当の ejb.jar ファイルと war ファイルが
deploy ディレクトリにコピーされる。
実行してみると (/step2 にアクセス) 初期画面は表示される物の
カウンタを表示させようとするとエラーになる。
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException
sample.web.CounterServlet.doGet(CounterServlet.java:45)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
javax.servlet.http.HttpServlet.service(HttpServlet.java:810)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
root cause
java.lang.ClassCastException
com.sun.corba.se.impl.javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:229)
javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:137)
sample.util.CounterUtil.getHome(CounterUtil.java:17)
sample.web.CounterServlet.getNextCounter(CounterServlet.java:22)
sample.web.CounterServlet.doGet(CounterServlet.java:37)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
step1 を undeploy してみるが、効果なし。
client ライブラリを war ファイルに強引に追加すると deploy 時に仕様違反の
エラーが発生。
これという解決策が見つからない。デプロイの仕方が悪いのではという意見もあった。
EJB を jar で クライアント部分を WAR で別々にデプロイするとまずいのだろうか?
古い版で試してみる。3.0.7 は 5.0 では実行できない。1.4 で実行は出来たが、
ビルドが失敗してしまう。3.2.0 を 1.4 で起動してみる。コンパイル、デプロイは
問題ない。
実行してみると 3.2.0 では確かに動く。 4.0.3 では異なる単位でデプロイされた
EJB は参照できないということか?
処理の流れ
index.jsp
<a href="CounterServlet">...</a>
CounterServlet.doGet()
getNextCounter()
// Home interface を取得して EJB を取得
get home interface
home.findByPrimaryKey(name) and return counter.getNextValue()
// request の attribute に ejb の値をセットし、jsp にフォワード
request.setAttribute(COUNTER_NAME, new Long(counter))
getServletContext().getRequestDispatcher("/ShowCounter.jsp")
ShowCounter.jsp
// スクリプトレットで値を取り出す。
<%=request.getAttribute("counter")%>
*** step 3
EAR ファイルでのデプロイ
xxapp.ear:
+- *.jar # EJB-JAR file
+- *.war # WAR file
+- META-INF
+- application.xml
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
"http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>Counter application</display-name>
<module>
<ejb>sample-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>sample.war</web-uri>
<context-root>/step3</context-root>
</web>
</module>
</application>
4.0.3SP1 ではやはり動かない...
4.0.3 の readme.html にある compatibility に関する記述を試してみる。
conf/jboss-service.xml
<mbean code="org.jboss.naming.NamingService"
name="jboss:service=Naming">
<!-- The call by value mode. true if all lookups are unmarshalled
using
the caller's TCL, false if in VM lookups return the value by reference.
-->
<attribute name="CallByValue">''true''</attribute> // SET TO TRUE
<!-- The listening port for the bootstrap JNP service. Set this to
-1
to run the NamingService without the JNP invoker listening port.
-->
...
</mbean>
deploy/ear-deployer.xml
<server>
<!-- EAR deployer, remove if you are not using ear deployments -->
<mbean code="org.jboss.deployment.EARDeployer"
name="jboss.j2ee:service=EARDeployer">
<!-- A flag indicating if ear deployments should have their own scoped
class loader to isolate their classes from other deployments.
-->
<attribute name="Isolated">true</attribute> // SET TO TRUE
<!-- A flag indicating if the ear components should have in VM call
optimization disabled.
-->
<attribute name="CallByValue">true</attribute> // SET TO TRUE
</mbean>
</server>
では、STEP 2 はどうか?
こちらも動くようになった。
4.0 ベースのサンプルというか、書き方を探す必要あり。
*** step 4
ENC (環境ネーミングコンテクスト) モジュール毎に JNDI 空間を割り当てる方法
"java:comp/env"
詳細は面倒なのでまたの機会に
** 3章 Open Source による開発 [#ba374075]
*** Ant
特になし。
*** XDoclet
ひとつの Bean クラスのソースから、各種インターフェイスの java source, ejb-jar.xml, jboss.xml, jbosscmp-jdbc.xml 等を生成。
/**
* カウンターを表すエンティティBeanです。
*
* @ejb.bean
* name="Counter"
* type="CMP"
* jndi-name="ejb/step5/counter"
* local-jndi-name="ejb/step5/counterLocal"
* primkey-field="name"
*
* @ejb.finder
* signature="Collection findAll()"
* description="すべてのカウンターを返す"
*
* @ejb.persistence table-name="step5"
* @ejb.persistence create-table="${jboss.create.table}"
* @ejb.persistence remove-table="${jboss.remove.table}"
*/
public abstract class CounterBean implements EntityBean {
..
* @ejb.interface-method
* @ejb.persistent-field
*/
public abstract String getName();
/**
* 名前
*
* @param name 名前
*/
public abstract void setName(String name);
/**
* カウンタ値
*
* @return カウンタ値
* @ejb.interface-method
* @ejb.persistent-field
* @jboss.column-name name="cvalue"
* use different column name for value property
*/
public abstract long getValue();
/**
* カウンタ値
*
* @param value カウンタ値
* @ejb.interface-method
*/
public abstract void setValue(long value);
:
* @param name 名前
* @param value カウンタ値
* @return 名前
* @ejb.create-method
*/
public String ejbCreate(String name, long value) throws CreateException {
if (name == null) {
throw new CreateException("name is null");
}
setName(name);
setValue(value);
return null;
}
ejbdoclet target の定義
<!-- ==================== XDoclet ========================= -->
<target name="ejbdoclet" depends="init">
<delete dir="${build.gen}"/>
<mkdir dir="${build.gen}"/>
<!-- ejbdocletサブタスクの宣言 -->
<taskdef
name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="ejbdoclet.classpath"/>
<!-- ejbdocletの起動-->
<ejbdoclet
destdir="${build.gen}"
excludedtags="@version,@author,@todo"
ejbspec="${ejb.version}"
mergedir="${project.root}/xdoclet"
force="${xdoclet.force}"
>
<!-- Beanクラスファイルの指定 -->
<fileset dir="${src.dir}">
<include name="**/sample/ejb/*.java"/>
</fileset>
<!-- リモートインタフェースの生成 -->
<remoteinterface/>
<homeinterface/>
<!-- ローカルインタフェースの生成 -->
<localinterface/>
<localhomeinterface/>
<!-- セッションBeanクラスの生成 -->
<session/>
<!-- データオブジェクトクラスの生成 -->
<dataobject pattern="{0}DTO"/>
<!-- エンティティCMPクラスの生成 -->
<entitycmp/>
<!-- <entitypk/>-->
<!-- <entityfacade/>-->
<!-- <remotefacade/>-->
<!-- <entitybmp/>-->
<!-- <dao/>-->
<!-- ユーティリティクラスの生成 -->
<utilobject cacheHomes="true"/>
<!-- デプロイメント記述子の生成 -->
<deploymentdescriptor
xmlencoding="${xml.encoding}"
destdir="${build.dir}/META-INF"/>
<!-- JBossデプロイメント記述子の生成 -->
<jboss version="${jboss.version}"
xmlencoding="${xml.encoding}"
destdir="${build.dir}/META-INF"
validateXml="false"/>
</ejbdoclet>
</target>
XDoclet は何かとっつきにくく、得体が知れないところがあり、積極的には
使ってこなかった。とはいえ、web.xml 等を見慣れてきたとはいえ、そらで
かけるわけではない。ここは慣れておいたほうがよさそう。
*** NetBean, Eclipse との連携
http://jboss.sourceforge.net/jbosside/updates
DTD に基づいた自動補完機能は活用していきたい。いくつか書きたい DTD もあるし。
** section 4
*** build
build 出来るはずだが...
BUILD FAILED
E:\ghjbossbook\samples\chap4\build.xml:120: E:\ghjbossbook\samples\chap4
\${env.XDOCLET_HOME}\lib not found.
http://xdoclet.sourceforge.net/xdoclet/index.html
からダウンロードできるかとも思ったが、すでに展開してあったのでそこを指定。
*** conf/login-config.xml の変更
login、認証の種類、その付属情報の設定。このケースでは使用する DB の指定、
セレクト方法などを追加する。
*** load-data に -Dhsqldb.port=1701
port が 3.0.7 から変わったのかも
4.0.3 でもポートは 1701 の様だが、接続が出来ない。
BUILD FAILED
E:\ghjbossbook\samples\chap4\build.xml:358: java.sql.SQLException: socket creati on error
E:\ghjbossbook\samples\chap4>java -cp c:\usr\local\jboss\server\default\lib\hsqldb.jar org.hsqldb.util.DatabaseManager
jdbc:hsqldb:hsql://localhost:1701 に接続しようとすると SQLException が
発生する。
DefaultDS で conf 以下を検索。 standardjbosscmp-jdbc.xml に気になる記述を
見つける
<defaults>
<datasource>java:/DefaultDS</datasource>
<!-- optional since 4.0 <datasource-mapping>Hypersonic SQL</datasource-mapping> -->
<create-table>true</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
<read-time-out>300000</read-time-out>
3.2.0 では
<defaults>
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>Hypersonic SQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
4.0.3 の datasource-mapping を有効にすることで直りそうだが、では、DefaultDS
はどうなっているのか? JBoss のマニュアルをよく読まないと...
コメントをはずしても依然として接続できない。
たしか、docs/examples/jca の下の *-ds.xml を元にデータソースの定義を
deploy かどこかに作る必要があったはず。
hsqldb-ds.xml は deploy にあったが次の行がコメントになっていた。
コメントをはずす。
<connection-url>jdbc:hsqldb:hsql://localhost:1701</connection-url>
しかし、それでもだめ。
起動時にクライアントサイドのコードが起動され SQLException に至る物が
多数表示されるようになった。
hsqldb-jdbc3-service.xml を deploy にコピーしてみてもだめ
standardjbosscmp-jdbc.xml から datasource-mapping をはずす。
hsqldb-ds.xml から connection-url をはずす。
これで元に戻ったはず。
もう一度、関連する xml ファイルを見直してみる。hsqldb-ds.xml 後半に次の
ようなブロックがある。
<!-- This mbean should be used only when using tcp connections. Uncomment
when the tcp based connection-url is used.
<mbean code="org.jboss.jdbc.HypersonicDatabase"
name="jboss:service=Hypersonic">
<attribute name="Port">1701</attribute>
<attribute name="Silent">true</attribute>
<attribute name="Database">default</attribute>
<attribute name="Trace">false</attribute>
<attribute name="No_system_exit">true</attribute>
</mbean>
-->
standardjbosscmp-jdbc.xml で datasource-mapping を有効に、
hsqldb-ds.xml で connection-url を有効にし、上記ブロックも有効にする。
再起動すると今回は HSQLDB connect のスタックとレースは出ない。
DatabaseManager からもコネクトできる。
ここで、再度 ant load-data を実行。今度は成功!
book-lib を再デプロイ
console に EJB-QL のエラーがでた
01:11:15,027 WARN [ServiceController] Problem starting service jboss.j2ee:jndiName=ejb/library/pass,service=EJB
org.jboss.deployment.DeploymentException: Error compiling EJB-QL statement ''; - nested throwable: (org.jboss.ejb.plugins.cmp.ejbql.ParseException: FROM not found)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCEJBQLQuery.<init>(JDBCEJBQLQuery.java:52)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCCommandFactory.createEJBQLQuery(JDBCCommandFactory.java:60)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCQueryManager.start(JDBCQueryManager.java:272)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.startStoreManager(JDBCStoreManager.java:490)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.start(JDBCStoreManager.java:381)
...
ログインを試みると成功。
しかし、図書サービスは発注を除きすべてエラーになる。
exception
javax.servlet.ServletException
library.book.web.LibraryServlet.getDestinationPage(LibraryServlet.java:64)
library.book.web.LibraryServlet.doGet(LibraryServlet.java:39)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
javax.servlet.http.HttpServlet.service(HttpServlet.java:810)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
root cause
library.book.web.LibrarySystemException
library.book.web.LibraryBusinessDelegate.listBooks(LibraryBusinessDelegate.java:137)
library.book.web.ListBooksAction.perform(Configuration.java:113)
library.book.web.LibraryServlet.performAction(LibraryServlet.java:73)
library.book.web.LibraryServlet.getDestinationPage(LibraryServlet.java:60)
library.book.web.LibraryServlet.doGet(LibraryServlet.java:39)
public Collection listBooks() throws LibrarySystemException {
try {
return getBookSearch().listBooks(); <--- ???
} catch (Exception e) {
throw new LibrarySystemException(e);
}
}
server.log からさかのぼると、たどることの出来るもっとも深い例外は
java.rmi.ServerException: EJBException:; nested exception is:
javax.ejb.EJBException: null; CausedByException is:
Could not instantiate bean; CausedByException is:
createBeanClassInstanceCommand == null; CausedByException is:
Could not instantiate bean; CausedByException is:
createBeanClassInstanceCommand == null
at org.jboss.ejb.plugins.LogInterceptor.handleException(LogInterceptor.java:352)
at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:196)
at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invoke(ProxyFactoryFinderInterceptor.java:122)
at org.jboss.ejb.SessionContainer.internalInvoke(SessionContainer.java:624)
at org.jboss.ejb.Container.invoke(Container.java:873)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:245)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
at org.jboss.invocation.local.LocalInvoker$MBeanServerAction.invoke(LocalInvoker.java:155)
at org.jboss.invocation.local.LocalInvoker.invoke(LocalInvoker.java:104)
at org.jboss.invocation.InvokerInterceptor.invokeMarshalled(InvokerInterceptor.java:201)
at org.jboss.invocation.MarshallingInvokerInterceptor.invoke(MarshallingInvokerInterceptor.java:35)
at org.jboss.proxy.TransactionInterceptor.invoke(TransactionInterceptor.java:46)
at org.jboss.proxy.SecurityInterceptor.invoke(SecurityInterceptor.java:55)
at org.jboss.proxy.ejb.StatelessSessionInterceptor.invoke(StatelessSessionInterceptor.java:97)
at org.jboss.proxy.ClientContainer.invoke(ClientContainer.java:86)
at $Proxy87.listBooks(Unknown Source)
at library.book.web.LibraryBusinessDelegate.listBooks(LibraryBusinessDelegate.java:135)
at library.book.web.ListBooksAction.perform(Configuration.java:113)
おそらく、EJB-QL の問題を解決すれば直るのだろうが、今日は時間切れ。
なれと経験の問題だろうが、何をやるにもわかりにくいし面倒くさい。
* 技評の JBoss 入門
JBoss site のマニュアルから入るのがよいのかもしれないが、
ざっと全体を眺めるため、日本語の入門本で動作を確認してみる。
ベースが 3.0.7/3.2.0 なので 4.0.3 SP1 で動くか少し心配。
細かいエラーを覚えてないが、4.0.3 でなかなか消えないエラーが SP1 にしたことで
あっさり消えたので、今回も SP1 を使用した。
** 1章
JBoss モジュールの紹介や、EJB の基礎
*** EJB の種類
いまさらですが...
- Session Bean
-- javax.ejb.SessionBean
- Entity Bean
-- javax.ejb.EntityBean
- Message Driven Bean
*** 必須インターフェイス
- Component Interface
- Home Interface
- Bean class
- Deployment Descriptor ejb-jar.xml
*** EJB-JAR file structure
xxejb.jar
+- Sample.class # component interface
+- SampleHome.class # home interface
+- SampleBean.class # EJB Bean
+- META-INF
+- ejb-jar.xml
ejb-jar のサンプルは #step1 内にある
*** EJB Interface: Remote and Local
| | Component Interface | Home Interface |
| Remote Interface | javax.ejb.EJBObject | javax.ejb.EJBHome |
| Local Interface | javax.ejb.EJBLocalObject | javax.ejb.EJBLocalHome |
*** JBoss deployment descriptor
- jboss.xml
-- JNDI name 等 ejb-jar.xml にかけない設定情報を記述する. WEB-INF におく
- jboss-web.xml
-- JNDI name 等 web.xml に記述できない設定情報を記述
- jbosscmp-jdbx.cml
-- CMP Entity EJB module 用。データソース名、フィールドと SQL タイプの
マッピングなど
** 2章 プログラミングの基礎
*** step1
最初のサンプル。簡単な CMP Entity Bean を jar + META-INF/ejb-jar.xml
形式でデプロイする。クライアントサイドはスタンドアローンプログラム。
: Counter | Component interface, extends EJBObject, getValue(), setValue()
などを定義するインターフェイス
: CounterHome | Home interface, extends EJBHome, create(), findByPrimaryKey(),
findAll() 等を定義するインターフェイス
: CounterBean | Entity Bean, public abstract class CounterBean implements EntityBean,
永続化フィールドに関する setter, getter を abstract で定義. ''ejbCreate''
をはじめ、ejbActivate(), ejbPassivate(), ejbLoad(), ejbStore, ejbRemove()
等が定義される。
: CounterClient | JNDI で Home インターフェイスを取得し、プライマリキーで
エンティティビーンをみつけて、なければ作成。findAll() ですべてのエンティティ
を見つけて表示する。
: step1-ejb.jar | jar file, web.xml は入っていない。純粋に EJB のみを含む。
META-INF/
META-INF/MANIFEST.MF
sample/
sample/ejb/
META-INF/ejb-jar.xml
META-INF/jboss.xml
META-INF/jbosscmp-jdbc.xml
sample/ejb/Counter.class
sample/ejb/CounterBean.class
sample/ejb/CounterHome.class
deploy 時のメッセージ
17:36:35,948 INFO [ProxyFactory] Bound EJB Home 'Counter' to jndi 'ejb/step1/co
unter'
17:36:36,620 INFO [EJBDeployer] Deployed: file:/C:/usr/local/jboss/server/defau
lt/deploy/step1-ejb.jar
: ejb-jar.xml | Entity Bean の定義
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<home>sample.ejb.CounterHome</home>
<remote>sample.ejb.Counter</remote>
<ejb-class>sample.ejb.CounterBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<abstract-schema-name>Counter</abstract-schema-name>
<cmp-field><field-name>name</field-name></cmp-field>
<cmp-field><field-name>value</field-name></cmp-field>
<primkey-field>name</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>
:jboss.xml | EJB と JNDI の対応付け
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC
"-//JBoss//DTD JBOSS//EN"
"http://www.jboss.org/j2ee/dtd/jboss.dtd">
<jboss>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<jndi-name>ejb/step1/counter</jndi-name>
</entity>
</enterprise-beans>
</jboss>
:jbosscmp-jdbc.xml|CMP bean を DB にどのようにおくかを指定
<?xml version="1.0"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC
"-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN"
"http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">
<jbosscmp-jdbc>
<defaults>
<create-table>true</create-table>
<remove-table>true</remove-table>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Counter</ejb-name>
<table-name>step1</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
:jndi.properties | 以下の内容の物でコンパイルの過程でクラスのディレクトリ
にコピーされているが jar には含まれていない。動いてしまうのはこれらが
デフォルトと一致しているためか... いや、違う、これはクライアントプログラム
のための物だ。
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
実行してみるとエラーになった。
E:\ghjbossbook\samples\chap2\step1>step1
[test]
javax.naming.CommunicationException [Root exception is java.io.InvalidClassException: org.jboss.invocation.InvokerInterceptor; unable to create instance]
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:707)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:572)
at javax.naming.InitialContext.lookup(Unknown Source)
at sample.client.CounterClient.getHome(CounterClient.java:48)
at sample.client.CounterClient.main(CounterClient.java:19)
Caused by: java.io.InvalidClassException: org.jboss.invocation.InvokerInterceptor; unable to create instance
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.Interceptor.readExternal(Interceptor.java:66)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at org.jboss.proxy.ClientContainer.readExternal(ClientContainer.java:142)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at java.rmi.MarshalledObject.get(Unknown Source)
at org.jnp.interfaces.MarshalledValuePair.get(MarshalledValuePair.java:57)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:637)
... 4 more
ふと思い出したが、client の jar ファイルの構成が変わっていたためにトラブルが
あったような気がした。確かに実行ファイルにしても実行用の ant task にしても
クラスパスに通す jar ファイルはハードコーディングしてある。
次の様なタスクを加えて実行すると正しく動作した。
<target name="runclient">
<java classname="sample.client.CounterClient" fork="yes" dir="${build.classes}">
<classpath>
<fileset dir="${jboss.home}/client">
<include name="**/*.jar"/>
</fileset>
<pathelement location="${build.classes}"/>
</classpath>
</java>
</target>
runclient:
[java] log4j:WARN No appenders could be found for logger (org.jboss.security.SecurityAssociation).
[java] log4j:WARN Please initialize the log4j system properly.
[java] name=counter,value=2
直ったけれど、率直な感想としてもっとわかりやすいエラーにしてほしい。
ネストした例外をすべて出せるかどうかわからないが、jar ファイルが足りず
エラーになるということは ClassNotFoundException が大本ではないの?
とするとそのクラス名から jar ファイルが足りないという線は直ぐにでて
きそうな物だが...
*** Step 2
Web コンテナから実行する方法の解説
: war file | 次の様な構成をとる
xxxweb.war # .war で終わればよい
+- WEB-INF
| +- web.xml
+- classes/ # class files
+- lib/ # jar files
+- <HTML, JSP, image file 等>
: web.xml | ServletContext init param, session config, servlet/jsp name
ビルド前の構成
│ build.xml
│
├─build
│ ├─classes
│ │ │ jndi.properties
│ │ │
│ │ ├─META-INF
│ │ │ ejb-jar.xml
│ │ │ jboss.xml
│ │ │ jbosscmp-jdbc.xml
│ │ │
│ │ └─sample
│ │ ├─ejb
│ │ │ Counter.class
│ │ │ CounterBean.class
│ │ │ CounterHome.class
│ │ │
│ │ ├─util
│ │ │ CounterUtil.class
│ │ │
│ │ └─web
│ │ CounterServlet.class
│ │
│ └─lib
│ sample-ejb-client.jar
│ step2-ejb.jar
│ step2.war
│
├─lib
│ javax.servlet.jar
│
├─resources
│ jndi.properties
│
└─src
└─sample
├─ejb
│ │ Counter.java
│ │ CounterBean.java
│ │ CounterHome.java
│ │
│ └─META-INF
│ ejb-jar.xml
│ jboss.xml
│ jbosscmp-jdbc.xml
│
├─util
│ CounterUtil.java
│
└─web
│ CounterServlet.java
│ index.jsp
│ ShowCounter.jsp
│
└─WEB-INF
web.xml
ant で build すると step1 相当の ejb.jar ファイルと war ファイルが
deploy ディレクトリにコピーされる。
実行してみると (/step2 にアクセス) 初期画面は表示される物の
カウンタを表示させようとするとエラーになる。
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException
sample.web.CounterServlet.doGet(CounterServlet.java:45)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
javax.servlet.http.HttpServlet.service(HttpServlet.java:810)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
root cause
java.lang.ClassCastException
com.sun.corba.se.impl.javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:229)
javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:137)
sample.util.CounterUtil.getHome(CounterUtil.java:17)
sample.web.CounterServlet.getNextCounter(CounterServlet.java:22)
sample.web.CounterServlet.doGet(CounterServlet.java:37)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
step1 を undeploy してみるが、効果なし。
client ライブラリを war ファイルに強引に追加すると deploy 時に仕様違反の
エラーが発生。
これという解決策が見つからない。デプロイの仕方が悪いのではという意見もあった。
EJB を jar で クライアント部分を WAR で別々にデプロイするとまずいのだろうか?
古い版で試してみる。3.0.7 は 5.0 では実行できない。1.4 で実行は出来たが、
ビルドが失敗してしまう。3.2.0 を 1.4 で起動してみる。コンパイル、デプロイは
問題ない。
実行してみると 3.2.0 では確かに動く。 4.0.3 では異なる単位でデプロイされた
EJB は参照できないということか?
処理の流れ
index.jsp
<a href="CounterServlet">...</a>
CounterServlet.doGet()
getNextCounter()
// Home interface を取得して EJB を取得
get home interface
home.findByPrimaryKey(name) and return counter.getNextValue()
// request の attribute に ejb の値をセットし、jsp にフォワード
request.setAttribute(COUNTER_NAME, new Long(counter))
getServletContext().getRequestDispatcher("/ShowCounter.jsp")
ShowCounter.jsp
// スクリプトレットで値を取り出す。
<%=request.getAttribute("counter")%>
*** step 3
EAR ファイルでのデプロイ
xxapp.ear:
+- *.jar # EJB-JAR file
+- *.war # WAR file
+- META-INF
+- application.xml
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
"http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>Counter application</display-name>
<module>
<ejb>sample-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>sample.war</web-uri>
<context-root>/step3</context-root>
</web>
</module>
</application>
4.0.3SP1 ではやはり動かない...
4.0.3 の readme.html にある compatibility に関する記述を試してみる。
conf/jboss-service.xml
<mbean code="org.jboss.naming.NamingService"
name="jboss:service=Naming">
<!-- The call by value mode. true if all lookups are unmarshalled
using
the caller's TCL, false if in VM lookups return the value by reference.
-->
<attribute name="CallByValue">''true''</attribute> // SET TO TRUE
<!-- The listening port for the bootstrap JNP service. Set this to
-1
to run the NamingService without the JNP invoker listening port.
-->
...
</mbean>
deploy/ear-deployer.xml
<server>
<!-- EAR deployer, remove if you are not using ear deployments -->
<mbean code="org.jboss.deployment.EARDeployer"
name="jboss.j2ee:service=EARDeployer">
<!-- A flag indicating if ear deployments should have their own scoped
class loader to isolate their classes from other deployments.
-->
<attribute name="Isolated">true</attribute> // SET TO TRUE
<!-- A flag indicating if the ear components should have in VM call
optimization disabled.
-->
<attribute name="CallByValue">true</attribute> // SET TO TRUE
</mbean>
</server>
では、STEP 2 はどうか?
こちらも動くようになった。
4.0 ベースのサンプルというか、書き方を探す必要あり。
*** step 4
ENC (環境ネーミングコンテクスト) モジュール毎に JNDI 空間を割り当てる方法
"java:comp/env"
詳細は面倒なのでまたの機会に
** 3章 Open Source による開発
*** Ant
特になし。
*** XDoclet
ひとつの Bean クラスのソースから、各種インターフェイスの java source, ejb-jar.xml, jboss.xml, jbosscmp-jdbc.xml 等を生成。
/**
* カウンターを表すエンティティBeanです。
*
* @ejb.bean
* name="Counter"
* type="CMP"
* jndi-name="ejb/step5/counter"
* local-jndi-name="ejb/step5/counterLocal"
* primkey-field="name"
*
* @ejb.finder
* signature="Collection findAll()"
* description="すべてのカウンターを返す"
*
* @ejb.persistence table-name="step5"
* @ejb.persistence create-table="${jboss.create.table}"
* @ejb.persistence remove-table="${jboss.remove.table}"
*/
public abstract class CounterBean implements EntityBean {
..
* @ejb.interface-method
* @ejb.persistent-field
*/
public abstract String getName();
/**
* 名前
*
* @param name 名前
*/
public abstract void setName(String name);
/**
* カウンタ値
*
* @return カウンタ値
* @ejb.interface-method
* @ejb.persistent-field
* @jboss.column-name name="cvalue"
* use different column name for value property
*/
public abstract long getValue();
/**
* カウンタ値
*
* @param value カウンタ値
* @ejb.interface-method
*/
public abstract void setValue(long value);
:
* @param name 名前
* @param value カウンタ値
* @return 名前
* @ejb.create-method
*/
public String ejbCreate(String name, long value) throws CreateException {
if (name == null) {
throw new CreateException("name is null");
}
setName(name);
setValue(value);
return null;
}
ejbdoclet target の定義
<!-- ==================== XDoclet ========================= -->
<target name="ejbdoclet" depends="init">
<delete dir="${build.gen}"/>
<mkdir dir="${build.gen}"/>
<!-- ejbdocletサブタスクの宣言 -->
<taskdef
name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="ejbdoclet.classpath"/>
<!-- ejbdocletの起動-->
<ejbdoclet
destdir="${build.gen}"
excludedtags="@version,@author,@todo"
ejbspec="${ejb.version}"
mergedir="${project.root}/xdoclet"
force="${xdoclet.force}"
>
<!-- Beanクラスファイルの指定 -->
<fileset dir="${src.dir}">
<include name="**/sample/ejb/*.java"/>
</fileset>
<!-- リモートインタフェースの生成 -->
<remoteinterface/>
<homeinterface/>
<!-- ローカルインタフェースの生成 -->
<localinterface/>
<localhomeinterface/>
<!-- セッションBeanクラスの生成 -->
<session/>
<!-- データオブジェクトクラスの生成 -->
<dataobject pattern="{0}DTO"/>
<!-- エンティティCMPクラスの生成 -->
<entitycmp/>
<!-- <entitypk/>-->
<!-- <entityfacade/>-->
<!-- <remotefacade/>-->
<!-- <entitybmp/>-->
<!-- <dao/>-->
<!-- ユーティリティクラスの生成 -->
<utilobject cacheHomes="true"/>
<!-- デプロイメント記述子の生成 -->
<deploymentdescriptor
xmlencoding="${xml.encoding}"
destdir="${build.dir}/META-INF"/>
<!-- JBossデプロイメント記述子の生成 -->
<jboss version="${jboss.version}"
xmlencoding="${xml.encoding}"
destdir="${build.dir}/META-INF"
validateXml="false"/>
</ejbdoclet>
</target>
XDoclet は何かとっつきにくく、得体が知れないところがあり、積極的には
使ってこなかった。とはいえ、web.xml 等を見慣れてきたとはいえ、そらで
かけるわけではない。ここは慣れておいたほうがよさそう。
*** NetBean, Eclipse との連携
http://jboss.sourceforge.net/jbosside/updates
DTD に基づいた自動補完機能は活用していきたい。いくつか書きたい DTD もあるし。
** section 4
*** build
build 出来るはずだが...
BUILD FAILED
E:\ghjbossbook\samples\chap4\build.xml:120: E:\ghjbossbook\samples\chap4
\${env.XDOCLET_HOME}\lib not found.
http://xdoclet.sourceforge.net/xdoclet/index.html
からダウンロードできるかとも思ったが、すでに展開してあったのでそこを指定。
*** conf/login-config.xml の変更
login、認証の種類、その付属情報の設定。このケースでは使用する DB の指定、
セレクト方法などを追加する。
*** load-data に -Dhsqldb.port=1701
port が 3.0.7 から変わったのかも
4.0.3 でもポートは 1701 の様だが、接続が出来ない。
BUILD FAILED
E:\ghjbossbook\samples\chap4\build.xml:358: java.sql.SQLException: socket creati on error
E:\ghjbossbook\samples\chap4>java -cp c:\usr\local\jboss\server\default\lib\hsqldb.jar org.hsqldb.util.DatabaseManager
jdbc:hsqldb:hsql://localhost:1701 に接続しようとすると SQLException が
発生する。
DefaultDS で conf 以下を検索。 standardjbosscmp-jdbc.xml に気になる記述を
見つける
<defaults>
<datasource>java:/DefaultDS</datasource>
<!-- optional since 4.0 <datasource-mapping>Hypersonic SQL</datasource-mapping> -->
<create-table>true</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
<read-time-out>300000</read-time-out>
3.2.0 では
<defaults>
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>Hypersonic SQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
4.0.3 の datasource-mapping を有効にすることで直りそうだが、では、DefaultDS
はどうなっているのか? JBoss のマニュアルをよく読まないと...
コメントをはずしても依然として接続できない。
たしか、docs/examples/jca の下の *-ds.xml を元にデータソースの定義を
deploy かどこかに作る必要があったはず。
hsqldb-ds.xml は deploy にあったが次の行がコメントになっていた。
コメントをはずす。
<connection-url>jdbc:hsqldb:hsql://localhost:1701</connection-url>
しかし、それでもだめ。
起動時にクライアントサイドのコードが起動され SQLException に至る物が
多数表示されるようになった。
hsqldb-jdbc3-service.xml を deploy にコピーしてみてもだめ
standardjbosscmp-jdbc.xml から datasource-mapping をはずす。
hsqldb-ds.xml から connection-url をはずす。
これで元に戻ったはず。
もう一度、関連する xml ファイルを見直してみる。hsqldb-ds.xml 後半に次の
ようなブロックがある。
<!-- This mbean should be used only when using tcp connections. Uncomment
when the tcp based connection-url is used.
<mbean code="org.jboss.jdbc.HypersonicDatabase"
name="jboss:service=Hypersonic">
<attribute name="Port">1701</attribute>
<attribute name="Silent">true</attribute>
<attribute name="Database">default</attribute>
<attribute name="Trace">false</attribute>
<attribute name="No_system_exit">true</attribute>
</mbean>
-->
standardjbosscmp-jdbc.xml で datasource-mapping を有効に、
hsqldb-ds.xml で connection-url を有効にし、上記ブロックも有効にする。
再起動すると今回は HSQLDB connect のスタックとレースは出ない。
DatabaseManager からもコネクトできる。
ここで、再度 ant load-data を実行。今度は成功!
book-lib を再デプロイ
console に EJB-QL のエラーがでた
01:11:15,027 WARN [ServiceController] Problem starting service jboss.j2ee:jndiName=ejb/library/pass,service=EJB
org.jboss.deployment.DeploymentException: Error compiling EJB-QL statement ''; - nested throwable: (org.jboss.ejb.plugins.cmp.ejbql.ParseException: FROM not found)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCEJBQLQuery.<init>(JDBCEJBQLQuery.java:52)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCCommandFactory.createEJBQLQuery(JDBCCommandFactory.java:60)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCQueryManager.start(JDBCQueryManager.java:272)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.startStoreManager(JDBCStoreManager.java:490)
at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.start(JDBCStoreManager.java:381)
...
ログインを試みると成功。
しかし、図書サービスは発注を除きすべてエラーになる。
exception
javax.servlet.ServletException
library.book.web.LibraryServlet.getDestinationPage(LibraryServlet.java:64)
library.book.web.LibraryServlet.doGet(LibraryServlet.java:39)
javax.servlet.http.HttpServlet.service(HttpServlet.java:697)
javax.servlet.http.HttpServlet.service(HttpServlet.java:810)
org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
root cause
library.book.web.LibrarySystemException
library.book.web.LibraryBusinessDelegate.listBooks(LibraryBusinessDelegate.java:137)
library.book.web.ListBooksAction.perform(Configuration.java:113)
library.book.web.LibraryServlet.performAction(LibraryServlet.java:73)
library.book.web.LibraryServlet.getDestinationPage(LibraryServlet.java:60)
library.book.web.LibraryServlet.doGet(LibraryServlet.java:39)
public Collection listBooks() throws LibrarySystemException {
try {
return getBookSearch().listBooks(); <--- ???
} catch (Exception e) {
throw new LibrarySystemException(e);
}
}
server.log からさかのぼると、たどることの出来るもっとも深い例外は
java.rmi.ServerException: EJBException:; nested exception is:
javax.ejb.EJBException: null; CausedByException is:
Could not instantiate bean; CausedByException is:
createBeanClassInstanceCommand == null; CausedByException is:
Could not instantiate bean; CausedByException is:
createBeanClassInstanceCommand == null
at org.jboss.ejb.plugins.LogInterceptor.handleException(LogInterceptor.java:352)
at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:196)
at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invoke(ProxyFactoryFinderInterceptor.java:122)
at org.jboss.ejb.SessionContainer.internalInvoke(SessionContainer.java:624)
at org.jboss.ejb.Container.invoke(Container.java:873)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:245)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
at org.jboss.invocation.local.LocalInvoker$MBeanServerAction.invoke(LocalInvoker.java:155)
at org.jboss.invocation.local.LocalInvoker.invoke(LocalInvoker.java:104)
at org.jboss.invocation.InvokerInterceptor.invokeMarshalled(InvokerInterceptor.java:201)
at org.jboss.invocation.MarshallingInvokerInterceptor.invoke(MarshallingInvokerInterceptor.java:35)
at org.jboss.proxy.TransactionInterceptor.invoke(TransactionInterceptor.java:46)
at org.jboss.proxy.SecurityInterceptor.invoke(SecurityInterceptor.java:55)
at org.jboss.proxy.ejb.StatelessSessionInterceptor.invoke(StatelessSessionInterceptor.java:97)
at org.jboss.proxy.ClientContainer.invoke(ClientContainer.java:86)
at $Proxy87.listBooks(Unknown Source)
at library.book.web.LibraryBusinessDelegate.listBooks(LibraryBusinessDelegate.java:135)
at library.book.web.ListBooksAction.perform(Configuration.java:113)
おそらく、EJB-QL の問題を解決すれば直るのだろうが、今日は時間切れ。
なれと経験の問題だろうが、何をやるにもわかりにくいし面倒くさい。