ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

本気で取り組むEJB!コミュのOpenEJBの情報求む

  • mixiチェック
  • このエントリーをはてなブックマークに追加
なぜかマニュアルが存在しない…
一部あるけどメチャ古い…

ホントにApache財団のトップ・プロジェクトなん?ww

そんな彼(OpenEJB)ですが、がんばって解析していきましょうグッド(上向き矢印)グッド(上向き矢印)


1環境構築方法
2動作不良、対策

これらが出ると嬉しいですね、ちなみに管理人はEclipseにて動作させ、JBossとコンテナの動作比較とかやってます。
同じソースでも希望の動作しないこともしばしばなので、あまり実用には耐えれないのかな?とか思ってみたりあせあせ

それではみなさん、よろしくお願いいたします m(__)m

コメント(14)

【Tomcat + OpenEJB】にて必要になる諸設定ファイルの例でも書きますか。

■1つ目
2.x系の「openejb-jar.xml」

--------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
// ? DTD記述場所

<!-- ejbを一意に識別するためのIDを付与 -->
<openejb-jar id="jp.co.stady.hoclic"> ←?定義の仕方

<!--
 1 (必須) ejb-nameにはejb-jar.xmlで定義している名前を記述
 2 (任意) deployment-idはOpenEJB内で一意になる名前
 3 (必須) container-idはどの定義を当てはめるか
 4 (必須) jndi,interfaceは呼び出し名
-->
<ejb-deployment
 ejb-name="Account"
 deployment-id="Account"
 container-id="Default BMP Container" >

 <jndi name="ejb/AccountHome" interface="hoge.bmp.AccountHome" />
 <jndi name="ejb/AccountLocalHome" interface="hoge.bmp.AccountLocalHome" />
 <resource-link res-ref-name="jdbc/MySQL" res-id="jdbc/MySQL"/> ←?リソース

</ejb-deployment>

</openejb-jar>
--------------------------------------------------------------------

それでは順に解説します。

?DTDは存在しないのでは?1年前から探してるけど見つかりません。
?ear内で重複しない一意なIDをつけられるはずですが…
?EJB(A)から別EJB(B)を呼出す場合、EJB(B)がデータソースを修得するために必須です。ejb-jar.xmlで

<resource-ref>
 <res-ref-name>jdbc/MySQL</res-ref-name>
 <res-type>javax.sql.DataSource</res-type>
 <res-auth>Container</res-auth>
</resource-ref>

としていると思うので、その時の「res-ref-name」を記述します。
ちなみに記述しないとContext#lookupでデータソースが取得できません。
■2つ目
2.x系の「jndi.properties」

--------------------------------------------------------------------
#Factory
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory ←?ファクトリー
#DataSource
movieDatabase=new://Resource?type=DataSource
movieDatabase.JdbcDriver=com.mysql.jdbc.Driver
movieDatabase.JdbcUrl=jdbc:mysql://localhost/hoge
movieDatabase.username=root
movieDatabase.password=

#その他
OpenEJB.level = WARNING
OpenEJB.server.level = INFO
OpenEJB.startup.level = INFO
OpenEJB.startup.config.level = INFO
OpenEJB.hsql.level = INFO
OpenEJB.handlers = 9OpenEJB.org.apache.juli.FileHandler
CORBA-Adapter.hsql.level = ERROR
CORBA-Adapter.handlers = 9OpenEJB.org.apache.juli.FileHandler
Transaction.level = WARNING
Transaction.handlers = 9Transaction.org.apache.juli.FileHandler
org.apache.activemq.level = ERROR
org.apache.activemq.handlers = OpenEJB.org.apache.juli.FileHandler
org.apache.geronimo.level = ERROR
org.apache.geronimo.handlers = 9OpenEJB.org.apache.juli.FileHandler
openjpa.level = ERROR
openjpa.handlers = 9OpenEJB.org.apache.juli.FileHandler

9OpenEJB.org.apache.juli.FileHandler.level = FINE
9OpenEJB.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
9OpenEJB.org.apache.juli.FileHandler.prefix = openejb.
9Transaction.org.apache.juli.FileHandler.level = FINE
9Transaction.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
9Transaction.org.apache.juli.FileHandler.prefix = transaction.
--------------------------------------------------------------------

それでは解説…といきたいけど、必要なのは?だけですね。

型は2.x系と3.x系で違うようですが、継承関係にあるようなので apache.oepnejb. の方を使ってもらっても大丈夫です。(クラス名間違ってるかもw)
次に重要なのはこの記述は『ローカル』用であるということです。

リモートの場合はリモート用のクラスとポート指定を行います。

※=(イコール)の前後に空白があると認識されません、気をつけましょう。
■3つ目
Tomcat 5.5系の「context.xml」

ここの設定が一番大事かな?
順に行きましょう。

1.ソース
Context ctx = new InitialContext();
Object obj = (Object) ctx.lookup("java:comp/env/ejb/AccountHome");
AccountHome home = (AccountHome)PortableRemoteObject.narrow(obj, AccountHome.class);

2.web.xmlの参照
<ejb-ref>
 <ejb-ref-name>ejb/AccountHome</ejb-ref-name>
 <ejb-ref-type>Entity</ejb-ref-type>
 <home>hoge.bmp.AccountHome</home>
 <remote>hoge.bmp.Account</remote>
</ejb-ref>

ご覧んの通り、リモートアクセスです。

そして肝心のcontext.xml
--------------------------------------------------------------------
<Ejb
 name="ejb/AccountHome"
 auth="Container"
 type="Entity"
 description="EJB declaration"
 factory="org.openejb.client.TomcatEjbFactory"
 home="hoge.bmp.AccountHome"
 remote="hoge.bmp.Account"
 openejb.naming.factory.initial="org.apache.openejb.client.LocalInitialContextFactory"
 openejb.ejb-link="ejb/AccountHome"
/>
--------------------------------------------------------------------

お気づきですか?
web.xml→jndi空間→contextと参照が移り、その実態はTomcatが

openejb.naming.factory.initial="org.apache.openejb.client.LocalInitialContextFactory"
↑ローカルで作成し
openejb.ejb-link="ejb/AccountHome"
↑(内部的には)通常のContext#lookupでリモートオブジェクトを返している

こういう作りなんですね!

ちなみに<ejb-ref>内でejb-linkを使用した場合もcontext内の「ejb」タグが検索されます。
また本当にリモートで取得が必要な場合は、リモートファクトリーの指定と参照先のURL+ポートを指定します。

context.xmlにベタ書きなのがちょっと難ですが、ここを制御することで実ソース、web.xmlともに操作しなくても参照先を切り替えれる点が評価できます。
補足です。
EJBの仕様では

『同じear内に存在するejbについては「ejb-link」で直接参照可能』

とありますが(JBossではむしろear内はejb-linkで書けと強制されてます)、上記の記述方法だとear外であっても「ejb-link」使えちゃいますね。。。

これってどうなんでしょう?
分散環境において実モジュールがどこに存在するかを意識させないソース⇒デプロイヤのみが把握・管理するだけで良いという考え方は素晴らしいですが、逆に「何でもあり」な感じがします。

個人的にはJBossの制約の方が分かりやすくて好きですねあせあせ(飛び散る汗)
>>2

貼り付ける内容間違ってた…
すいません(汗)

■jndi.properties
--------------------------------------------------------------------
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory

openejb.home=C:/openejb-3.1.2
openejb.base=C:/user/workspace_SCS/WebProject/WebContent/WEB-INF/lib

openejb.configuration=C:/user/workspace_SCS/WebProject/src/openejb.xml

log4j.category.OpenEJB.options = debug
log4j.category.OpenEJB.startup = debug
log4j.category.OpenEJB.startup.config = debug

openejb.jndiname.format = {ejbName}/{interfaceClass}
openejb.descriptors.output = true
openejb.validation.output.level = verbose
--------------------------------------------------------------------

でしたw
■4つ目

大事な事忘れてました。
jarの配置をメモします。

Tomcat/lib
? openejb-3.X.X\lib\○○.jar 全部

・EclipseプロジェクトのWebContent/lib
? openejb-3.X.X\lib\○○.jar 全部

それらを紐づける設定ファイル達

「openejb.xml」
--------------------------------------------------------------------
<?xml version="1.0"?>

<openejb>
↓?
<Container id="Default CMP Container" ctype="CMP_ENTITY" />
<Container id="Default BMP Container" ctype="BMP_ENTITY" />
<Container id="Default Stateful Container" ctype="STATEFUL" />
<Container id="Default Stateless Container" ctype="STATELESS" />

<!--
 jndi.propertiesで指定しても良いし、ここで下記の記述をしても有効
 id は任意と思われる
-->
↓?
<Resource id="jdbc/MySQL" type="DataSource">
 JdbcDriver com.mysql.jdbc.Driver
 JdbcUrl jdbc:mysql://localhost:3306/hoge
 UserName root
 Password
 JtaManaged true
</Resource>
↓?
<Deployments jar="/BMP-Entity.jar" />

</openejb>
--------------------------------------------------------------------

?は各定義。指定しないとデフォルトが適用されます。
?データソースの定義です。
?別プロジェクトで作成したjarを指定します。ここはEclipseで開発するならばlib配下に置くのが良いかと。
jndi.propertiesのopenejb.baseで指定したディレクトリと相対です。

上記の設定に倣えば、java.net.URLClassLoaderとかorg.apache.catalina.loader.StandardClassLoaderとかSystemLoaderとか、詠み込みによるClassCastExceptionは回避できます。
■定番のXDoxletを使いこなすための一歩

build.xml
--------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>

 <project name="Loan" default="generate">
 <!--
 <property name="xdoclet-home" value="C:/Work/Xdocle/xdoclet-1.2.3"/>
 -->
 <property name="xdoclet-home" value="C:/Work/Xdocle/xdoclet-1.2.2"/>
 <property name="openejb.home" value="C:/Work/EJB/openejb-3.1.4"/>
 <property name="deploy.dir" value="${jboss.home}/jar"/>
 <property name="jar.name" value="loan.jar" />

 <!--EjbDoclet拡張タスクを定義(必要なクラスパスも指定) -->
 <path id="class.path">
 <fileset dir="${xdoclet-home}/lib">
  <include name="*.jar"/>
 </fileset>
 <!-- J2EEのAPI -->
 <pathelement path="${openejb.home}/lib/javaee-api-5.0-3.jar" />
 </path>

 <target name="clear">
 <delete dir="tempFolder"/>
 </target>

 <target name="init">
 <taskdef name="ejbdoclet"
 classname="xdoclet.modules.ejb.EjbDocletTask"
 classpathref="class.path"/>

 <mkdir dir="tempFolder/classes"/>
 <mkdir dir="tempFolder/ejbModule"/>
 <mkdir dir="tempFolder/ejbModule/META-INF"/>
 </target>

 <target name="generate" depends="clear,init">
 <!-- [destdir] 生成した各ファイルの配置場所を指定 -->
 <!-- [excludedtags] 処理対象から外すタグ -->
 <ejbdoclet
 destdir="tempFolder/ejbModule"
 excludedtags="@version,@author,@todo"
 ejbspec="2.1"
 >

 <!--
 【 対象のソースファイルを指定 】
 -->
 <fileset dir="ejbModule">
  <include name="**/*Bean.java"/>
 </fileset>

 <!--
 【 エンティティBean の生成 】
 -->
 <session />
 <entitycmp />
 <entitybmp />

 <!--
 【 インターフェース の生成 】
 -->
 <remoteinterface />
 <homeinterface/>
 <localinterface />
 <localhomeinterface />

 <!--
 【 ejb-jar.xmlの生成 】
 -->
 <deploymentdescriptor
  destdir="tempFolder/ejbModule/META-INF"
 />

 <!--
 【 JBoss固有のDDの生成 】
 ・version  JBossのバージョン
 ・destdir  DDを生成するディレクトリ
 ・datasource  データソース名
 ・datasourceMapping データベースタイプ
 -->
 <!--
 <jboss
  version="4.0"
  destdir="tempFolder/ejbModule/META-INF"
  datasource="java:/env/jdbc/MySQL"
  datasourceMapping="jdbc/MySQL"
 />
 -->
 <!--
 【 WebLogic固有のDDの生成 】
 -->
 <!--
 <weblogic
 version="6.1"
 destdir="${samples.meta-inf.dir}"
 datasource="java:/samplesDataSource"
 persistence="weblogic"
 />
 -->
 <!--
 【 OpenEJB固有のDDの生成 】
 -->
 <openejb
 destdir="tempFolder/ejbModule/META-INF"
 >
  <!-- ファイルの文字コードは引数で渡す仕様(指定されない場合は""とされる) -->
  <configParam name="Xmlencoding" value="UTF-8"/>
 </openejb>

 </ejbdoclet>
 <javac
  destdir="tempFolder/classes"
  classpathref="class.path"
  debug="on"
  deprecation="on"
  optimize="off"
 >
  <src path="ejbModule"/>
  <src path="tempFolder/ejbModule"/>
 </javac>
 </target>

 <target name="package">
 <zip destfile="tempFolder/${jar.name}">
 <zipfileset dir="tempFolder/classes" includes="**/*.class"/>
 <zipfileset dir="tempFolder/ejbModule/META-INF" prefix="META-INF/"/>
 </zip>
 </target>

 <target name="deploy" depends="package">
 <copy file="tempFolder/${jar.name}" todir="${deploy.dir}"/>
 </target>

 <target name="undeploy">
 <delete file="${deploy.dir}/${jar.name}"/>
 </target>

</project>
--------------------------------------------------------------------

使いまわせるように微妙にコメントアウトしてあります。
APIのjarのところを適当に変えるとイロイロなServerに対応できるはず。
同一の設定をするにも
1.XML
2.Properties
の2種類があるようだ。

例えば「インスタンス・プールの数を設定する」にも

<Container id="MyBMP" type="BMP_ENTITY">
PoolSize 1
</Container>

と

MyBMP=new://Container?type=BMP_ENTITY
MyBMP.PoolSize=1

どちらでも同じ結果だった。
ビジネスコンポーネント・トピの続きをここで。
・ejbPassivate()が呼ばれないってやつ

ワカタヨ、チャチョサン、理由、ワカタヨ

1.JBossで上手く動いていたのは何かしらのデフォルト設定のおかげっぽい
2.どちらかというとOpenEJBの動作の方が「フォロー」が無い分解析しやすい

以下検証途中ですが報告までに。

■Servlet → SessionBean → EntityBean
SessionBeanのトランザクション属性が「Container」かつ、EntityBeanのトランザクション属性が「Required」であった場合、自然とトランザクション管理がされる。
よって、正常に動作する。

■Servlet → EntityBean
パターンA(問題の現象)
EntityBeanのトランザクション属性が「Required」であった場合は親Servletがトランザクションを持っていないため、メソッドが呼ばれるごとに新しいトランザクションを作成しなければならない。
新しいオブジェクトは開始されたトランザクションの状態を保持し続けなければならないため、コンテナから削除されることはない(メモリ食いつぶすね)
次にServletから次のメソッド・コールが行われるが、上記の理由により再び新しいオブジェクトが作成され、トランザクションの状態を表現する。
上記の状態を繰り返す事から「都度新しいオブジェクトが作られる」という現象になっていたと思われる。

パターンB
ではトランザクション無しなら?とNotSupported属性にしたところ希望通りの順番でログが出力された。
やっぱり原因はそこらしい。

ではServlet自体からトランザクションを流そうかと思ったが、ソースの改修量が半端ないのでやめた。
でもこの仮説結構正しいと思う。
EJB内でデータソースを使いたい場合の方法。
『openejb-jar.xml』で『tomcat』のデータソースを指定してあげる必要があります。

<ejb-deployment ejb-name="Account"
deployment-id="Account"
container-id="MyBMP" >
<jndi name="ejb/AccountHome" interface="hoge.bmp.AccountHome" />
<jndi name="ejb/AccountLocalHome" interface="hoge.bmp.AccountLocalHome" />
<resource-link res-ref-name="jdbc/MySQL" res-id="jdbc/MySQL"/>
</ejb-deployment>
●resource-linkタグを付与(これがないとurl=nullで落ちる)

ちなみにopenejb.xml(紛らわしいからconfにしようかな…)
<Resource id="jdbc/MySQL" type="DataSource">
JdbcDriver com.mysql.jdbc.Driver
JdbcUrl jdbc:mysql://localhost:3306/hoge
UserName root
Password
JtaManaged false
</Resource>

と記述していた場合、ソースは

DataSource ds = (DataSource)ctx.lookup("openejb:Resource/jdbc/MySQL");
で取得可能です。

後者の場合はopenejb独自記述なのでresource-linkタグは書かなくても動作します=ちゃんとコネクション取得可能。

でも、汎用的って考えた場合はJNDIに乗っとるべきなんだろうね。
コンテナ依存は極力排除しようってことで。
メッセージドリブンBeanを使う場合の設定方法
【OpenEJB機能を使う場合】

■ejb-jar.xml
<message-driven>
 <display-name>QueueMDB</display-name>
 <ejb-name>QueueMDB</ejb-name>
 <ejb-class>mogo.ReceiverEJB</ejb-class>
 <transaction-type>Container</transaction-type>
 <message-driven-destination>
  <destination-type>javax.jms.Queue</destination-type>
 </message-driven-destination>
</message-driven>

■openejb-jar.xml
<ejb-deployment
 ejb-name="QueueMDB"
 deployment-id="QueueMDB"
 container-id="MyJmsMdbContainer" >
</ejb-deployment>

■openejb.xml
<Resource id="MyJmsResourceAdapter" type="ActiveMQResourceAdapter">
 BrokerXmlConfig
 ServerUrl tcp://localhost:61616(activeMQを想定)
</Resource>

<Resource id="MyJmsConnectionFactory" type="javax.jms.ConnectionFactory">
 ResourceAdapter MyJmsResourceAdapter
</Resource>

<Container id="MyJmsMdbContainer" ctype="MESSAGE">
 ResourceAdapter MyJmsResourceAdapter
</Container>

<Resource id="QueueMDB" type="javax.jms.Queue" />

■src
javax.jms.Queue queue = (Queue) context.lookup("openejb:Resource/QueueMDB");

以上です。
メッセージドリブンBeanを使う場合の設定方法
【JNDIを使う場合】

■Tomcat#context.xml
<Resource
 name="jms/ConnectionFactory"
 auth="Container"
 type="org.apache.activemq.ActiveMQConnectionFactory"
 description="JMS Connection Factory"
 factory="org.apache.activemq.jndi.JNDIReferenceFactory"
 scheme=""
 brokerURL="failover:(tcp://localhost:61616)"
 brokerName="LocalActiveMQBroker"
/>
<Resource
 name="jms/someTopic"
 auth="Container"
 type="org.apache.activemq.command.ActiveMQTopic"
 description="my Topic"
 factory="org.apache.activemq.jndi.JNDIReferenceFactory"
 physicalName="hoge"
/>

<Resource
 name="jms/QueueMDB"
 auth="Container"
 type="org.apache.activemq.command.ActiveMQQueue"
 description="my Queue"
 factory="org.apache.activemq.jndi.JNDIReferenceFactory"
 physicalName="QueueMDB"
/>

■web.xml
<resource-ref>
 <res-ref-name>jms/aQueue</res-ref-name>
 <res-type>javax.jms.Queue</res-type>
 <res-auth>Container</res-auth>
 <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref>
 <res-ref-name>jms/someTopic</res-ref-name>
 <res-type>javax.jms.Topic</res-type>
 <res-auth>Container</res-auth>
 <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

<message-destination-ref>
 <message-destination-ref-name>jms/QueueMDB</message-destination-ref-name>
 <message-destination-type>javax.jms.Queue</message-destination-type>
 <message-destination-usage>ConsumesProduces</message-destination-usage>
 <message-destination-link>QueueMDB</message-destination-link>
</message-destination-ref>
<message-destination>
 <message-destination-name>QueueMDB</message-destination-name>
</message-destination>

■src
javax.jms.Queue queue = (Queue) context
 .lookup("java:comp/env/jms/QueueMDB");

これが出来るようになるまで丸2日かかりました。
チョイ補足と修正

<!--
【brokerURLの疑問】
ここの設定値は「実参照」がされない限り接続先の起動有無は確認されない。
そのためOpenEJBの起動でまずActiveMQが起動され、その後ロジック→実参照が
行われる。よって、本記載の「brokerURL」は下記、どちらでも動作することになる。
1.failover:(tcp://localhost:61616) = すでに起動されているので参照可能
2.vm://localhost = 同一のVM上を参照する
内部で起動するか外部で起動するかを気にしなくて良い「1」の方が設定の手間が
少ない。※URL・ポートが変更される可能性がある場合は大差ないのかもしれないけど
-->
<Resource
name="jms/ConnectionFactory"
auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="failover:(tcp://localhost:61616)"
brokerName="LocalActiveMQBroker"
/>
スキーマ要素は間違い、ないです。
CMPを動かそうとしたら出た。

ERROR - JAVA AGENT NOT INSTALLED. The JPA Persistence Provider requested installation of a ClassFileTransformer which requires a JavaAgent. See http://openejb.apache.org/3.0/javaagent.html

スタンドアローンじゃなくてサーバーで起動してるんですけど?
なんで出るの?

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

本気で取り組むEJB! 更新情報

本気で取り組むEJB!のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング