J2EE notes

「J2EE notes」の編集履歴(バックアップ)一覧はこちら

J2EE notes」(2006/09/01 (金) 19:02:21) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

** Session Bean アプリケーションサーバーに対して単一クライアントとして振舞う。 複雑なビジネスロジックの入り口としての役割を果たす。 ある時点においてセッションビーンはクライアントに共有されない。 パーシステントではない。クライアントがいなくなると意味は持たない。 *** Stateless Session Beans ステートレスセッションビーンはクライアントの状態にかかわる情報を 保持しない。ステートレスセッションビーンのメソッドの実行中には そのインスタンス変数にクライアントのステートにかかわる情報を保持 することもあるが、そのメソッドが終わると意味は持たない。コンテナは ステートレスセッションビーンをどのクライアントにもアサインできる。 ステートレスセッションビーンのみが Web Service を実装できる。 *** Stateful Session Beans クライアントの状態に関する情報をインスタンス変数に持つことが出来る。 クライアントがビーンを削除したり、セッションが終わると状態はなくなる。 ** Entity Bean 永続化されるビジネスオブジェクトを表すビーン。通常リレーショナル データベースに保持され、あるビーンはテーブル内の行に対応する。 セッションビーンとは次の様な点で異なる。 - 永続化される - (複数のクライアントから)共有されることがある - プライマリキーを持つ - 他のエンティティビーンと参照関係を持つことがある 永続化には Bean Managed と Container Managed がある。 ** Message-Driven Bean JMS Message リスナーとして振舞う。アプリケーションクライアント、 エンタープライズビーン、Web コンポーネントなどがメッセージを 送ることができる。 ** J2EE実習 Enterprise/Enterprise Application からプロジェクトを作成。 ConverterApp という名前にする。 デフォルトでは ConverterApp-EJBModule, ConverterApp-WebModule も 作成される *** Session Bean の作成 ConverterApp-EJBModule で New -> Session Bean, 名前を Converter とする。 属性は stateless, remote とする。package は converter とする。 ConverterBean.java, ConverterRemote.java, ConverterRemoteBusiness.java, ConverterRemoteHome.java が作成される。 : ConverterBean.java | EJB の実装. implements SessionBean, ConverterRemoteBusiness SessionContext をプロパティとして持つ。 : ConverterRemote.java | EJB のリモートインターフェイス。クライアントは このインターフェイスを介して EJB にアクセスする。 extends EJBObject, ConverterRemoteBusiness . ビジネスメッソッドの インターフェイスは ConverterRemoteBusiness にまとめてある。 : ConverterRemoteBusiness.java | ビジネスインターフェイス。最初は から。 : ConverterRemoteHome.java | リモートホームインターフェイス。 extends EJBHome . ConverterRemote create() throws CreateException, RemoteException; のみが定義されている。 *** Business method の追加 ConverterApp-EJBModule -> Enterprise Bean -> ConverterSB -> add -> Business method メッソッド名 dollarToYen, return type BigDecimal, parameter dollors: BigDecimal とする。 メッソッド yenToEuro, return type BigDecimal. parameter yen: BigDecimal も追加する。 ConverterBean.java に次のインスタンス変数を加える。 BigDecimal yenRate = new BigDecimal("121.6000"); BigDecimal euroRate = new BigDecimal("0.0077"); メソッドのボディを書き換える。 public BigDecimal dollarToYen(BigDecimal dollars) { BigDecimal result = dollars.multiply(yenRate); return result.setScale(2, BigDecimal.ROUND_UP); } public BigDecimal yenToEuro(BigDecimal yen) { BigDecimal result = yen.multiply(euroRate); return result.setScale(2, BigDecimal.ROUND_UP); } *** Web Client の作成 ConverterApp-WebModule->New->Servlet, name=ConverterServlet, package= converter *** Locating home interface ConverterServlet のボディーで right-click -> Enterprise Resource-> Call Enterpriese Bean -> ConverterSB. lookupConverterBean が 自動生成される。 *** processRequest に EJB をアクセスするコードを追加 out.println("<h1><b><center>Converter</center></b></h1>"); out.println("<hr>"); out.println("<p>Enter an amount to convert:</p>"); out.println("<form method=\"get\">"); out.println("<input type=\"text\" name=\"amount\" size=\"25\">"); out.println("<br>"); out.println("<p>"); out.println("<input type=\"submit\" value=\"Submit\">"); out.println("<input type=\"reset\" value=\"Reset\">"); out.println("</form>"); String amount = request.getParameter("amount"); if ( amount != null && amount.length() > 0 ) { try { converter.ConverterRemote converter; converter = lookupConverterBean(); java.math.BigDecimal d = new java.math.BigDecimal(amount); out.println("<p>"); out.println("<p>"); out.println(amount + " Dollars are " + converter.dollarToYen(d) + " Yen."); out.println("<p>"); out.println(amount + " Yen are " + converter.yenToEuro(d) + " Euro."); converter.remove(); } catch (Exception e){ out.println("Cannot lookup or execute EJB!"); } } *** Run アプリケーションの相対 URI を /ConverterServlet に設定して 実行すると Servlet が表示され、変換も動作する。 ConverterApp/dist/converterapp.ear を JBoss 403 に deploy すると そのまま動作する。 *** Exception EJBException は RuntimeException にラップされる。このシステムエラーは クライアントではハンドリングできない。 アプリケーション例外にはユーザーが定義した物と javax.ejb に属する 例外がある。これらは適切にハンドリングされなければならない。 システムエラーの場合にはトランザクションはロールバックされるが、 アプリケーション例外の場合には行われない。 ** JBoss deploy error *** JNDI error jndi.properties で解決 # Sample ResourceBundle properties file java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099 java.naming.factory.url=org.jboss.naming:org.jnp.interfaces *** java.sql.SQLException: Table not found in statement [...] jboss.xml が存在していなかった *** javax.ejb.EJBException: ejbCreate: Unable to connect to database.Could not dereference object コネクション作成のための情報が不足している。 Sun AS の場合 setup/connection-pool-derby_net.sun-resource に設定が ある。 <?xml version="1.0" encoding="UTF-8"?> <resources> <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.ClientDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" max-pool-size="32" max-wait-time-in-millis="60000" name="derby_netConnectionPool" pool-resize-quantity="2" res-type="javax.sql.DataSource" steady-pool-size="8"> <property name="connectionAttributes" value=";create=true"/> <property name="serverName" value="localhost"/> <property name="PortNumber" value="1527"/> <property name="DatabaseName" value="sun-appserv-samples"/> <property name="User" value="APP"/> <property name="Password" value="APP"/> </jdbc-connection-pool> </resources> JBoss のデータソース設定方法を確認... ejb-jar.xml に書くデータ参照はあくまで参照名まで。参照名の定義は サーバー固有のファイルに書くことになる。サーバー毎に柔軟に Connection Pool を作り込めるためと考えれば納得できないこともない。 ejb-jar.xml 例: ... <entity> <display-name>SavingsAccountEB</display-name> <ejb-name>SavingsAccountBean</ejb-name> <home>bank.SavingsAccountRemoteHome</home> <remote>bank.SavingsAccountRemote</remote> <ejb-class>bank.SavingsAccountBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java.lang.String</prim-key-class> <reentrant>false</reentrant> <resource-ref> <description>jdbc:derby://localhost:1527/sun-appserv-samples;create=true [APP の APP]</description> <res-ref-name>jdbc/myDatabase</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> </entity> jboss.xml も JDBC driver name, url までは持っていない。これらは サーバーのリソースということだ。jca の定義ファイルを適切に直して deploy, driver クラスを用意して再起動する必要がある。 driver name, url, これらを基にしたコネクションプールの管理は Sun AS でもサーバー管理下にあるのは同じ事。紛らわしいのはその 定義のコピーが NetBeans project 内に作られること。それは情報の コピーということであり、その project ないの情報がコネクションプール 作成に直接影響するわけではないだろう。 jboss.xml: <jboss> <enterprise-beans> <entity> <ejb-name>SavingsAccountBean</ejb-name> <jndi-name>ejb/SavingsAccountBean</jndi-name> <resource-ref> <res-ref-name>jdbc/myDatabase</res-ref-name> <resource-name>DataSource1</resource-name> </resource-ref> </entity> </enterprise-beans> <resource-managers> <resource-manager> <res-name>DataSource1</res-name> <res-jndi-name>java:/MySqlDS</res-jndi-name> </resource-manager> </resource-managers> </jboss> この例では java:/MySqlDS がサーバーのデータソースの名前。 derby-ds.xml を deploy にコピーして Embeded 用の設定をクライアント 用に書き換える。Embeded 用だとソケット経由の URL を認識しないため。 derbyclient.jar を server/<type>/lib にコピーして、再起動。 server/<type>/lib 以下は hot-deploy ではないようだ。 これらを行っても使えるようにならなかった。デバッグ文を入れると JNDI lookup で失敗しているようだ。Sun の BMP の SavingsAccount はサンプルのためか、このあたりの細かいログは掃いてくれないため、 デバッグ文を入れて初めて(予想はしていたが..)判明。 server.log をよく見ると Derby のデータソースが一旦作られたが、JMX にマッピングをとろうと する段階で問題があったのか削除されているように見える。Embeded 用の ドライバを想定しているコードがあるのかも知れない。JMX は便利そうで 実はあまりそうでもないので JMX に結びつける文を derby-ds.xml からはずす。 再度挑戦、しかし今度はドライバをインスタンスかしている途中で ClassCastException がおきている。Derby JDBC driver で 実装し忘れているインターフェイスがあるのか、基底クラスの 互換問題かむづかしそう。jboss のサイトでその例外を投げている メソッドを見てみるが、キャストは (Property) ぐらいにしかやって ない。これもドライバのつくりの問題を感じさせる。ただ、型が シンプルなのでそのメソッドをデバッグすれば何かわかるかも知れないが、 JBoss のビルド以前途中で挫折した記憶があり、一旦保留。 ここまでの過程で DB を変えるために必要なステップもおのずと明らかに なってきた。複数の実行環境から参照できるように MySQL を使うことに した。ユーザー、DB,Table の作り方を復習しながら app server 用の テーブルとユーザーを用意する。テーブル作成の SQL は若干の手直しが 必要だった。'constraint <sym>' の部分がエラーになってしまうためだ。 これをとってテーブルは作成できた。 あとは mysql-ds.xml を書き換え deploy にコピー。 Connector/J の jar ファイルを server/<type>/lib にコピー、jboss.xml の java:/<DB ID> を MySQL 用に直し、再起動。 今度は動いた。半日以上棒に振った気がするが、よい勉強になったとも いえる。(最近は直ぐに忘れてしまうのだが...)
** Session Bean アプリケーションサーバーに対して単一クライアントとして振舞う。 複雑なビジネスロジックの入り口としての役割を果たす。 ある時点においてセッションビーンはクライアントに共有されない。 パーシステントではない。クライアントがいなくなると意味は持たない。 *** Stateless Session Beans ステートレスセッションビーンはクライアントの状態にかかわる情報を 保持しない。ステートレスセッションビーンのメソッドの実行中には そのインスタンス変数にクライアントのステートにかかわる情報を保持 することもあるが、そのメソッドが終わると意味は持たない。コンテナは ステートレスセッションビーンをどのクライアントにもアサインできる。 ステートレスセッションビーンのみが Web Service を実装できる。 *** Stateful Session Beans クライアントの状態に関する情報をインスタンス変数に持つことが出来る。 クライアントがビーンを削除したり、セッションが終わると状態はなくなる。 ** Entity Bean 永続化されるビジネスオブジェクトを表すビーン。通常リレーショナル データベースに保持され、あるビーンはテーブル内の行に対応する。 セッションビーンとは次の様な点で異なる。 - 永続化される - (複数のクライアントから)共有されることがある - プライマリキーを持つ - 他のエンティティビーンと参照関係を持つことがある 永続化には Bean Managed と Container Managed がある。 ** Message-Driven Bean JMS Message リスナーとして振舞う。アプリケーションクライアント、 エンタープライズビーン、Web コンポーネントなどがメッセージを 送ることができる。 ** J2EE実習 Enterprise/Enterprise Application からプロジェクトを作成。 ConverterApp という名前にする。 デフォルトでは ConverterApp-EJBModule, ConverterApp-WebModule も 作成される *** Session Bean の作成 ConverterApp-EJBModule で New -> Session Bean, 名前を Converter とする。 属性は stateless, remote とする。package は converter とする。 ConverterBean.java, ConverterRemote.java, ConverterRemoteBusiness.java, ConverterRemoteHome.java が作成される。 : ConverterBean.java | EJB の実装. implements SessionBean, ConverterRemoteBusiness SessionContext をプロパティとして持つ。 : ConverterRemote.java | EJB のリモートインターフェイス。クライアントは このインターフェイスを介して EJB にアクセスする。 extends EJBObject, ConverterRemoteBusiness . ビジネスメッソッドの インターフェイスは ConverterRemoteBusiness にまとめてある。 : ConverterRemoteBusiness.java | ビジネスインターフェイス。最初は から。 : ConverterRemoteHome.java | リモートホームインターフェイス。 extends EJBHome . ConverterRemote create() throws CreateException, RemoteException; のみが定義されている。 *** Business method の追加 ConverterApp-EJBModule -> Enterprise Bean -> ConverterSB -> add -> Business method メッソッド名 dollarToYen, return type BigDecimal, parameter dollors: BigDecimal とする。 メッソッド yenToEuro, return type BigDecimal. parameter yen: BigDecimal も追加する。 ConverterBean.java に次のインスタンス変数を加える。 BigDecimal yenRate = new BigDecimal("121.6000"); BigDecimal euroRate = new BigDecimal("0.0077"); メソッドのボディを書き換える。 public BigDecimal dollarToYen(BigDecimal dollars) { BigDecimal result = dollars.multiply(yenRate); return result.setScale(2, BigDecimal.ROUND_UP); } public BigDecimal yenToEuro(BigDecimal yen) { BigDecimal result = yen.multiply(euroRate); return result.setScale(2, BigDecimal.ROUND_UP); } *** Web Client の作成 ConverterApp-WebModule->New->Servlet, name=ConverterServlet, package= converter *** Locating home interface ConverterServlet のボディーで right-click -> Enterprise Resource-> Call Enterpriese Bean -> ConverterSB. lookupConverterBean が 自動生成される。 *** processRequest に EJB をアクセスするコードを追加 out.println("<h1><b><center>Converter</center></b></h1>"); out.println("<hr>"); out.println("<p>Enter an amount to convert:</p>"); out.println("<form method=\"get\">"); out.println("<input type=\"text\" name=\"amount\" size=\"25\">"); out.println("<br>"); out.println("<p>"); out.println("<input type=\"submit\" value=\"Submit\">"); out.println("<input type=\"reset\" value=\"Reset\">"); out.println("</form>"); String amount = request.getParameter("amount"); if ( amount != null && amount.length() > 0 ) { try { converter.ConverterRemote converter; converter = lookupConverterBean(); java.math.BigDecimal d = new java.math.BigDecimal(amount); out.println("<p>"); out.println("<p>"); out.println(amount + " Dollars are " + converter.dollarToYen(d) + " Yen."); out.println("<p>"); out.println(amount + " Yen are " + converter.yenToEuro(d) + " Euro."); converter.remove(); } catch (Exception e){ out.println("Cannot lookup or execute EJB!"); } } *** Run アプリケーションの相対 URI を /ConverterServlet に設定して 実行すると Servlet が表示され、変換も動作する。 ConverterApp/dist/converterapp.ear を JBoss 403 に deploy すると そのまま動作する。 *** Exception EJBException は RuntimeException にラップされる。このシステムエラーは クライアントではハンドリングできない。 アプリケーション例外にはユーザーが定義した物と javax.ejb に属する 例外がある。これらは適切にハンドリングされなければならない。 システムエラーの場合にはトランザクションはロールバックされるが、 アプリケーション例外の場合には行われない。 ** JBoss deploy error *** JNDI error jndi.properties で解決 # Sample ResourceBundle properties file java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099 java.naming.factory.url=org.jboss.naming:org.jnp.interfaces *** java.sql.SQLException: Table not found in statement [...] jboss.xml が存在していなかった *** javax.ejb.EJBException: ejbCreate: Unable to connect to database.Could not dereference object コネクション作成のための情報が不足している。 Sun AS の場合 setup/connection-pool-derby_net.sun-resource に設定が ある。 <?xml version="1.0" encoding="UTF-8"?> <resources> <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.ClientDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" max-pool-size="32" max-wait-time-in-millis="60000" name="derby_netConnectionPool" pool-resize-quantity="2" res-type="javax.sql.DataSource" steady-pool-size="8"> <property name="connectionAttributes" value=";create=true"/> <property name="serverName" value="localhost"/> <property name="PortNumber" value="1527"/> <property name="DatabaseName" value="sun-appserv-samples"/> <property name="User" value="APP"/> <property name="Password" value="APP"/> </jdbc-connection-pool> </resources> JBoss のデータソース設定方法を確認... ejb-jar.xml に書くデータ参照はあくまで参照名まで。参照名の定義は サーバー固有のファイルに書くことになる。サーバー毎に柔軟に Connection Pool を作り込めるためと考えれば納得できないこともない。 ejb-jar.xml 例: ... <entity> <display-name>SavingsAccountEB</display-name> <ejb-name>SavingsAccountBean</ejb-name> <home>bank.SavingsAccountRemoteHome</home> <remote>bank.SavingsAccountRemote</remote> <ejb-class>bank.SavingsAccountBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java.lang.String</prim-key-class> <reentrant>false</reentrant> <resource-ref> <description>jdbc:derby://localhost:1527/sun-appserv-samples;create=true [APP の APP]</description> <res-ref-name>jdbc/myDatabase</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> </entity> jboss.xml も JDBC driver name, url までは持っていない。これらは サーバーのリソースということだ。jca の定義ファイルを適切に直して deploy, driver クラスを用意して再起動する必要がある。 driver name, url, これらを基にしたコネクションプールの管理は Sun AS でもサーバー管理下にあるのは同じ事。紛らわしいのはその 定義のコピーが NetBeans project 内に作られること。それは情報の コピーということであり、その project ないの情報がコネクションプール 作成に直接影響するわけではないだろう。 jboss.xml: <jboss> <enterprise-beans> <entity> <ejb-name>SavingsAccountBean</ejb-name> <jndi-name>ejb/SavingsAccountBean</jndi-name> <resource-ref> <res-ref-name>jdbc/myDatabase</res-ref-name> <resource-name>DataSource1</resource-name> </resource-ref> </entity> </enterprise-beans> <resource-managers> <resource-manager> <res-name>DataSource1</res-name> <res-jndi-name>java:/MySqlDS</res-jndi-name> </resource-manager> </resource-managers> </jboss> この例では java:/MySqlDS がサーバーのデータソースの名前。 derby-ds.xml を deploy にコピーして Embeded 用の設定をクライアント 用に書き換える。Embeded 用だとソケット経由の URL を認識しないため。 derbyclient.jar を server/<type>/lib にコピーして、再起動。 server/<type>/lib 以下は hot-deploy ではないようだ。 これらを行っても使えるようにならなかった。デバッグ文を入れると JNDI lookup で失敗しているようだ。Sun の BMP の SavingsAccount はサンプルのためか、このあたりの細かいログは掃いてくれないため、 デバッグ文を入れて初めて(予想はしていたが..)判明。 server.log をよく見ると Derby のデータソースが一旦作られたが、JMX にマッピングをとろうと する段階で問題があったのか削除されているように見える。Embeded 用の ドライバを想定しているコードがあるのかも知れない。JMX は便利そうで 実はあまりそうでもないので JMX に結びつける文を derby-ds.xml からはずす。 再度挑戦、しかし今度はドライバをインスタンスかしている途中で ClassCastException がおきている。Derby JDBC driver で 実装し忘れているインターフェイスがあるのか、基底クラスの 互換問題かむづかしそう。jboss のサイトでその例外を投げている メソッドを見てみるが、キャストは (Property) ぐらいにしかやって ない。これもドライバのつくりの問題を感じさせる。ただ、型が シンプルなのでそのメソッドをデバッグすれば何かわかるかも知れないが、 JBoss のビルド以前途中で挫折した記憶があり、一旦保留。 ここまでの過程で DB を変えるために必要なステップもおのずと明らかに なってきた。複数の実行環境から参照できるように MySQL を使うことに した。ユーザー、DB,Table の作り方を復習しながら app server 用の テーブルとユーザーを用意する。テーブル作成の SQL は若干の手直しが 必要だった。'constraint <sym>' の部分がエラーになってしまうためだ。 これをとってテーブルは作成できた。 あとは mysql-ds.xml を書き換え deploy にコピー。 Connector/J の jar ファイルを server/<type>/lib にコピー、jboss.xml の java:/<DB ID> を MySQL 用に直し、再起動。 今度は動いた。半日以上棒に振った気がするが、よい勉強になったとも いえる。(最近は直ぐに忘れてしまうのだが...) ---- SavingsAccount の ejbCreate では balance が負の場合には CreateException を throw するが、それ以外の場合には insertRow() で DB 格納処理に進む。すでに存在するユーザーID に対してこれを 行うと EJBException になる。分類上はこれはシステムエラーということに なり、コンテナはロールバックするし、クライアントには RemoteException, local には EJBException となる。では、ここで ID (primary key) の 有無を調べてアプリケーションエラーにしてよいのだろうか?おそらく、 そうしたほうがクライアントは書きやすくなる。しかし、ひとつ例外が おきる条件が増えることでもある。これはもっとサンプルや、資料を調べて 見ないと厳密な事は言えなそう。

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。