Seasar Conference 2006 Autumn受付中
私も申し込みました。一応定員があるようですので、行こうと思っている方は、早めに申し込むと良いかもしれません。
Tomcatのエラーログをeclipseのコンソールに出力する
Seasar2を使ったWebアプリケーションを作っていて、思うように動かない現象に何度か遭遇しました。原因はサーバ起動時にSeasar2の初期化に失敗しているだけなのですが、それがeclipseのコンソールに出ないので気がつくまでに時間がかかるというマヌケな状態に(^^;
で、server.xmlを編集すればeclipseのコンソールに出力できることが分かったので、一応メモしておきます。たぶん、常識なんだと思いますが。
<!-- Logger shared by all Contexts related to this virtual host. By default (when using FileLogger), log files are created in the "logs" directory relative to $CATALINA_HOME. If you wish, you can specify a different directory with the "directory" attribute. Specify either a relative (to $CATALINA_HOME) or absolute path to the desired directory.--> <Logger className="org.apache.catalina.logger.FileLogger" directory="logs" prefix="localhost_log." suffix=".txt" timestamp="true"/> <!-- 以下の設定を追加する --> <Logger className="org.apache.catalina.logger.SystemErrLogger" timestamp="true"/>
2006/09/21追記:Tomcatのバージョンは、5.0.28で試しています。
[Seasar2][S2Dao] Hot DeployでS2Daoが使えない件
先日のHot DeployでS2Daoを動かす件、さらに検証してみたのですが現状のS2DaoはHot Deployに対応していないのではないかという結論に達しました。(追記:間違いでした。) コミッタの方、間違っていたらご指摘ください。以下、調査結果です。
まず、問題の整理。S2Daoを使ってList型の戻り値で結果を返すメソッドを定義し、これをHot Deploy環境で利用するとListに入ってくるインスタンスのクラスローダと、PageやLogicなどの値を利用する側のクラスローダが異なってClassCastExceptionが発生するというものでした。この問題は1回目の実行時だけはクラスローダが一致するため発生しません。
この原因は、どうやらDaoMetaDataFactoryImplでDaoMetaDataをキャッシュしているためのようです。1回目の実行時に作られたDaoMetaDataはキャッシュされ2回目以降は、そのインスタンスが使われます。そして、戻り値の元になるBeanClassも一緒にキャッシュされるため、そこからインスタンス化されたクラスとHot Deployによりリクエスト時に作成されたクラスでクラスローダが異なるようです。
試しに、キャッシュしないようにgetDaoMetaDataの一部をコメントアウトしたところ、動くことは確認できました。もっとも、ソースを全部見たわけではないので、ここ以外にも問題があるかもしれません。
[Sesar2] Hot Deployを試す
※注意:この文書はJUNDUがソースを超ザックリと読んだだけの内容に基づいています。つまり、間違っている可能性がすごく高いので鵜呑みにしないでください。
時代の流れ(?)に乗って、Hot Deployを試してみました。1から全部作るのが面倒だったので、teeda-html-exampleにs2jsf-exampleに実装されているEmployee Managementを移植するという試みです。結論から言うと、あっさりと1度目の挑戦はClassCastExceptionにより失敗に終わりました。ともあれ、どこまで出来たかを書いてみます。
まず、Hot Deployについておさらい。Hot Deployは、サーバの再起動なしにクラスファイルの変更を動作中のアプリケーションに反映させるSeasar2の機能です。この機能は、テストをスムーズに行うことを目的としていて、本番では使わない機能のようです。あまり、この辺りは語られていない気がします。本番では、Hot Deployとdiconファイルを共有できるCool Deployを使うことが想定されているようです。(間違っていたら、ツッコミお願いします)
さて、このHot Deployを使う上で重要なのがs2container.diconから参照されるdiconファイルで、通常「ほにゃらら-hotdeploy.dicon」というファイル名にするようです。このほにゃらら-hotdeploy.diconファイルは標準で提供されるものをリネームして使うイメージのように感じますが、今のところ詳細不明ですね。teeda-html-example-hotdeploy.diconの場合は、各種コンポーネント用のCreatorが登録されていて、一番下のOndemandBehaviorに基準となるパッケージを定義してあります。
Hot Deployでは、Seasar2側で想定している命名規則に従ったファイルを各Creatorが想定するコンポーネントとして、S2コンテナにデプロイします。例えば、xxxDaoというクラス名であれば、Daoクラスとして登録するといった感じです。つまりCreatorの動作は、
そして、teeda-html-example-hotdeploy.diconから参照されているteeda-html-example-customizer.diconも重要です。ここには、
ここまでの内容からEmployee Managementを作るための情報がそろいました。そこで、まずviewフォルダ配下にemployeeSeasch.htmlというファイルを作成します。まだ、日付型へのコンバータやバリデータの使い方がよく分かっていないので、かなり適当な内容になってます(^^;
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Employee Management</title> </head> <body> <form id="employeeSearchForm"> <table> <tr> <td class="label">EmployeeNo</td> <td> <input type="text" id="empno" class="right"/> <span id="empnoMessage"/> </td> </tr> <tr> <td class="label">EmployeeName</td> <td> <input type="text" id="ename"/> <span id="enameMessage"/> </td> </tr> <tr> <td class="label">Job</td> <td> <input type="text" id="job"/> <span id="jobMessage"/> </td> </tr> <tr> <td class="label">Manager</td> <td> <input type="text" id="mgr" class="right"/> </td> </tr> <tr> <td class="label">HireDate</td> <td><input id="fromHiredate" type="text"/> 〜 <input id="toHiredate" type="text"/></td> </tr> <tr> <td class="label">Salary</td> <td><input id="fromSal" type="text" class="right"/> 〜 <input id="toSal" type="text" class="right"/></td> </tr> <tr> <td class="label">Department</td> <td> <select id="deptnoItems"> <option value="">Please select</option> <option value="10">ACCOUNTING</option> <option value="20">RESEARCH</option> <option value="30">SALES</option> <option value="40">OPERATIONS</option> </select> </td> </tr> </table> <input type="button" value="create" id="doCreate"/> <input type="button" value="search" id="doSearch"/> </form> </body> </html>
そして、このHTMLに対応するPageクラスをexamples.teeda.web.exployeeに作成します。今回は1回目の実験なのでActionやSearviceは作っていません(^^;;
package examples.teeda.web.employee; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import examples.teeda.entity.Department; import examples.teeda.logic.DepartmentLogic; public class EmployeeSearchPage { private List deptnoItems; private String empno; private String ename; private String job; private String mgr; private String fromHiredate; private String toHiredate; private String fromSal; private String toSal; private int deptno; private DepartmentLogic deptLogic; public String initialize() { System.out.println("initialize()"); deptnoItems = new ArrayList(); List deptList = deptLogic.getAllDepartments(); Iterator itr = deptList.iterator(); while(itr.hasNext()) { DeptnoDto dto = new DeptnoDto(); Department dept = (Department)itr.next(); dto.setValue(dept.getDeptno()); dto.setLabel(dept.getDname()); deptnoItems.add(dto); } return null; } public String doCreate() { System.out.println("doCreate()"); return null; } public String doSearch() { System.out.println("doSearch()"); return null; } // ... getter/setterは省略 }
<select>の内容を動的に作成するためにEntityがそのまま使いたかったのですが、ラベルや値をマッピングする方法が分からなかったので、とりあえずは詰め替えしてます。DeptnoDtoは以下のような感じです。
package examples.teeda.web.employee; public class deptnodto { private string label; private int value; public string getlabel() { return label; } public int getvalue() { return value; } public void setlabel(string label) { this.label = label; } public void setvalue(int value) { this.value = value; } }
あとは、s2jsf-exampleのLogic、Dao、Entityを参考に、それぞれのファイルを作成します。ポイントは、それぞれexamples.teeda.logic、examples.teeda.dao、examples.teeda.entityに作成することです。Hot Deployでは命名規則重要なので。
それから、S2Daoを使うためにdao.diconとs2-dao-1.0.35.jarを入れて、hsqldbの設定とかします。この辺も、S2Daoを使うとき、サンプルを動かすときに共通なので省略します。
最後に、app.diconとteeda-html-example-customizer.diconを書き換えます。まず、app.diconはj2ee.diconとdao.diconを使える用にincludeしています。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components> <include path="convention.dicon"/> <include path="aop.dicon"/> <include path="app_aop.dicon"/> <include path="teedaExtension.dicon"/> <include path="j2ee.dicon"/> <include path="dao.dicon"/> <include condition="#ENV != 'ut'" path="teeda-html-example-cooldeploy.dicon"/> </components>
そして、teeda-html-example.customizer.diconには、dao.interceptorとj2ee.requiredTxを使うようにしています。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components> <component name="defaultCustomizer" class="org.seasar.framework.container.autoregister.AspectCustomizer"> <property name="interceptorName">"aop.classLoaderAwareTraceInterceptor"</property> </component> <component name="commandAspectCustomizer" class="org.seasar.framework.container.autoregister.AspectCustomizer"> <property name="interceptorName">"aop.classLoaderAwareTraceInterceptor"</property> <property name="pointcut">"do.*, initialize"</property> </component> <component name="actionSupportAspectCustomizer" class="org.seasar.framework.container.autoregister.AspectCustomizer"> <property name="interceptorName">"app_aop.actionSupportInterceptor"</property> <property name="pointcut">"do.*, initialize"</property> </component> <component name="daoSupportAspectCustomizer" class="org.seasar.framework.container.autoregister.AspectCustomizer"> <property name="interceptorName">"dao.interceptor"</property> </component> <component name="transactionSupportAspectCustomizer" class="org.seasar.framework.container.autoregister.AspectCustomizer"> <property name="interceptorName">"j2ee.requiredTx"</property> </component> <component name="actionCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>commandAspectCustomizer</arg> </initMethod> <initMethod name="addCustomizer"> <arg>actionSupportAspectCustomizer</arg> </initMethod> </component> <component name="daoCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> <initMethod name="addCustomizer"> <arg>daoSupportAspectCustomizer</arg> </initMethod> </component> <component name="dtoCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> </component> <component name="dxoCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> </component> <component name="helperCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> </component> <component name="logicCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> <initMethod name="addCustomizer"> <arg>transactionSupportAspectCustomizer</arg> </initMethod> </component> <component name="pageCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> </component> <component name="serviceCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> <initMethod name="addCustomizer"> <arg>defaultCustomizer</arg> </initMethod> </component> <component name="interceptorCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> </component> <component name="validatorCustomizer" class="org.seasar.framework.container.autoregister.CustomizerChain"> </component> </components>
ここでのポイントは、daoSupportAspectCustomizerとtransactionSupportAspectCustomizerの2つです。これがInterceptorの設定をしてくれるようです。
というわけで、私がやったのは、ここまでです。これで画面の初期表示の時だけは正常に動きます。ところが2度目以降の画面表示(今は手抜きなのでボタンを押すと自画面に遷移する)をするとClassCastExceptionが発生します。
原因は、Daoが返す検索結果のEntityクラスとPage側が持っている(?)Entityクラスが別々のクラスローダにロードされる殻のようです。試しにClassLoaderのtoStringを表示してみました。まず1回目のとき。
Page: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@5fbbf3 Logic: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@5fbbf3 Department: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@5fbbf3 Result: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@5fbbf3
そして、エラーの出る2回目以降。
Page: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@1f71773 Logic: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@1f71773 Department: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@1f71773 Result: org.seasar.framework.container.hotdeploy.HotdeployClassLoader@5fbbf3
というわけで、検索結果は1回目と同じクラスローダが使われていますが、Pageなどは別のクラスローダに変わってしまうと。なんとなく、コンポーネントのScopeが関係しそうな機がするのですが、果たしてどうしたものか。長くなってしまったので、今日のところはここまで。
最近、気になったこと。
Seasarの公式サイトは、現在は独立した物になってますが、以前はSourceForge.jpを借りてました。SourceForge.jp内のプロジェクトのページには、いわゆる「引っ越しました」がありますが、プロジェクト概要には何も書いてないのですね。ファイルも古いままですし、ちょっとだけ気になりました。
個人的には、プロジェクト概要に公式サイトの移動を追記してあって、ファイルは可能であれば最新のものがアップされてミラーとして使えたらカッコイイかなぁと。まぁ、まだミラーしないと困るほどダウンロードが集中することはないとは思いますが。
ファイルの方は良いとして、プロジェクトの概要は直していただけるとうれしいかなぁと思うです。見た目重要という気がするので。
# ちなみに概要のところDIxAOPじゃなくてDI+AOPという、ちょっと時代を感じさせる表記になってますね(^^;
最近のマーケティングって
色々、考えるものですね。以下のキーワードがあちこちに書いてあって、何事かと思いました(^^;
マージンFXのひまわり証券さん、ニンテンドーDS Lite欲しい!
実際、私はこれ見てなんだろうと思って「マージンFXのひまわり証券」というキーワードでgoogle検索してサイトまで見に行ってしまいました。そこには何の説明もないので、ますます意味が分からず困惑しましたが…。
そんなわけで、Seasarには関係なくてゴメンナサイ。
追記:他の人と違って全体がキーワードにならなくて、またまた困惑。よく考えたらキーワードの種類を規制していた気がするから、そのせいのような気がします。面倒なので確認はしませんw