Super Agile Struts開発記その5 - BeanUtils
以前のエントリで、BeanUtilsとHOT deployをあわせて使うと、PropertyDescriptorが無駄にどんどんキャッシュされていってメモリリークする話を書きました。
http://d.hatena.ne.jp/higayasuo/20070725#1185340917
実は、問題はそれだけではなく、BeanUtilsは、publicフィールドをプロパティとして認識しないという問題もあります。メモリリークだけなら、どこかでクリアすれば良いだけなのですが、publicフィールドの場合は、どうしようもありません。
結局、BeanUtilsにJavaBeansのプロパティをアクセスされたら負けなのです。
最初に考えたのが、JavaBeansをDynaBeanでラップする方法。Struts単独なら、これでうまく行きますが、JSTLと組み合わせるとうまく行かない。なぜなら、JSTLはDynaBeanのことは(たぶん)知らないから。
で、今はどうしようとしているかというと、JavaBeansをMapでラップしようとしています。でも、単純にラップするだけだとうまく行かないのです。JavaBeansがネストしているときは、ネストしたJavaBeansもラップしないといけないから。後、Collectionや配列でその要素がJavaBeansの場合も、ラップしてあげないといけない。
これから、この辺のコードを書く予定です。
常にフィードバックが得られる状態を保つ
今回のSAStrutsのActionFormの機能を実装しようとしたときに、丸一日コンパイルが通らない状態が続いていました。DynaBeanの実装をどこに持たそうか悩んでいたためです。別にコードをあれこれ書いて試行錯誤していたわけではなく、StrutsとBeanUtilsのソースを何度も読みながら、最適なコードを頭の中で考えていたのです。
最初から、ほぼ完成形のソースを書こうとしていたため、手が止まってしまいました。手が止まるのは、あまり良い傾向ではありません。もちろん、考えもなくコードを書くのは良くないのですが、たいていは、全体のうち、一部は見えているところがあります。その一部分から実装してみて、そこからフィードバックを得るべきでした。
今回は、Strutsベースの開発なので、テストを通すだけでなく、Strutsを使ったWebからのテストも常にできる状態を保つべきでした。
迷ったコードは良くないことが多いので、スパッと捨てました。JavaBeansをMapやIteratorでラップする方法は、大体見えているので、その部分を実装して、Strutsで動かしてみて、フィードバックを得ることにします。
フィードバックを得られるまでの時間がかかる実装は、やはり問題がいろいろあります。多少、時間がかかるように見えても、フィードバックが得られる状態を保ちながら実装することが重要だと改めて感じました。
Super Agile Struts開発記その6 - JSTL連動
JSTLと完全に連動できるようになりました。もちろん、JSTLやELがpublicフィールドを認識するし、HOT deployでメモリリークも起きません。よくあるHelloWorldのサンプルはこんな感じ。
package tutorial.web.hello
@Result(path="/hello/world.jsp")
public class WorldAction {public String name = "Seasar2";
@Execute
String execute() {
return "success";
}
}
world.jsp
これで、ブラウザから/hello/world.doにアクセスすると
<html>
<body>
Hello ${name}
</body>
</html>
と表示されます。Actionの変更ももちろん、HOTに認識します。Actionのプロパティは、リクエストの属性にプロパティ名と同じ名前で設定されているので、${プロパティ名}で簡単にプロパティにアクセスできます。
Hello Seasar2
publicフィールドを持ったJavaBeansも、もちろんJSTLとELがHOTに認識できます。
Hoge.java
Actionクラスを次のように定義します。
public class Hoge {
public String aaa;
}
JSPは次のように定義します。
public List<Hoge> hogeList = new ArrayList<Hoge>();@Execute
public String execute() {
for (int i = 0; i < 3; i++) {
Hoge hoge = new Hoge();
hoge.aaa = "aaa" + i;
}
hogeList.add(hoge);
return "success";
}
これを表示させると
<c:forEach items="${hogeList}" var="hoge">
$(hoge.aaa)<br />
</c:forEach>
のようになります。次のようなネストも可能です。
aaa0
aaa1
aaa2
public List<List<Hoge>> hogeListList =
new ArrayList<List<Hoge>>();@Execute
public String execute() {
for (int i = 0; i < 10; i++) {
List<Hoge> l = new ArrayList<Hoge>();
for (int j = 0; j < 2; j++) {
Hoge hoge = new Hoge();
hoge.aaa = "aaa" + i + j;
l.add(hoge);
}
hogeListList.add(l);
}
return "success";
}
<c:forEach items="${hogeListList}" var="hogeList">
<c:forEach items="${hogeList}" var="hoge">
${hoge.aaa}
</c:forEach>
<br />
</c:forEach>