ラベル Struts2 の投稿を表示しています。 すべての投稿を表示
ラベル Struts2 の投稿を表示しています。 すべての投稿を表示

2011年8月1日月曜日

Struts2でJSPから静的クラスメソッドを呼び出す!

リファレンスを参照すると、静的なクラスメソッドは「@class@method(args)」で呼び出せるようですが、残念ながらうまくいきませんでした。

結局、Actionクラスに呼び出し専用のメソッドを用意して、JSPから使うというのが一番確実なようです。

時間があれば、もう少しトライしてみたいですが、「If you specify the class, you must give the fully qualified name.」なんて話だと、いちいちコーディングするのが面倒になるので、やっぱりActionクラスを使うのが一番簡単かもしれません。

2011年7月22日金曜日

staticなメソッドのデバッグ

単純な話ですが、staticなメソッドをEclips&Tomcatの環境でデバッグしていましたが、再起動しないとうまくデバッグできません。

クラスインスタンスが生成された後はstaticなんでソース修正してもうまく反映されないんですね。

ソースがないよと怒られてしまいました!

2011年7月21日木曜日

Hibernateのオブジェクトとセッションの微妙な関係

Hibernateでデータベースを管理していると、いまでのSQLによるプログラミングと微妙な違いがあって戸惑ってしまいます。


JavaとSQLでゴリゴリやっていた当時は、Java側のオブジェクトにはデータベースのレコードのコピーがあって、メソッドでSQLを実行させて、データベースとのやり取りを行っていたので、データベースの更新についてプログラム側で完全に制御することが出来ました。


ところがHibernateを利用すると、追加、削除についてはプログラム側で制御可能ですが、更新についてはプログラム側での制御が難しいようです。
その原因は、Hibernateの一時オブジェクト、永続化オブジェクト、分離オブジェクトの3種類のオブジェクトがあるからのようです。


一時オブジェクト:Javaでnewしたオブジェクト

永続化オブジェクト:HibernateのSessionで管理されているオブジェクト(データベースのレコードと関連している)

分離オブジェクト:データベースのレコードの内容を持っているけれどもHibernateのSessionで管理されていないオブジェクト


特にプログラム側で意識していないと永続化オブジェクトがごろごろ出来あがって、ついには

org.hibernate.NonUniqueObjectException?: a different object with the same identifier value was already associated with the session

という例外を起こしてしまいます。

また、永続化オブジェクトのフィールドを変更するとHibernateが適当なタイミングでデータベースをupdateしてくれます。これは、従来のJavaとSQLになれたプログラマには???なことになってしまいます。意識しないうちデータベースのレコードが更新されているので、ちょっとおかしなことになってしまう場合もあります。


今回、開発途中で発生した問題は、上記2点の問題で振り回されてしまいました。

1番目の問題については、更新前にキャッシュをクリアしてから、更新対象となるデータを再読み込みすれば良いのでそれ程問題ではありませんでした。

問題だったのはHibernateの自動更新機能で、今回はプログラムの作成上、永続化オブジェクトのフィールドを変更した後に、特定の条件によってはその更新を行わない必要が出たのです。フィールドを変更する時に条件を判定して変更しないようにすれば問題は解決するのですが、データの編集を行うクラスと、実際に更新するクラスが異なるので、フィールド編集時には変更が必要かは判定できない仕様となっています。

いろいろと試行錯誤を行った結果以下の手法をとることにしました。

フィールド編集クラスで分離オブジェクトにして(evictメソッド参照)HibernateのSession管理からはずすことで、自動更新を回避します。

更新側では、更新の必要があった場合は、渡されたオブジェクトをupdateすることで永続化オブジェクトにして更新を行います。


Hibernateの管理ははずれるのでHibernateのメリットが犠牲になるような気もしますが、この方法が一番すっきりとできたようです。

2011年7月4日月曜日

Struts2のタグで引数の指定

Struts2のタグでは「value="hoge"」のように指定すると、JSPの表示要求をしたActionクラスのgetHoge()メソッドを呼び出してくれます。ところが、場合によってはメソッドに引数を指定したくなる場合があります。

今回は一覧標示をする時に、表示をするかの判定を行うメソッドで引数の指定が必要となってしまいました。

<s:iterator id="t" value="searchResult">
 <s:if test="#t.display">
  ここで表示する内容を編集
 </s:if>
</s:iterator>

のような記述が普通ですが、ここで検索結果の各要素(t)のisDisplay()メソッドに引数を渡す必要が発生したわけです。
いろいろと試した結果、以下のようにメソッド名を省略せずに指定することで引数を渡すことができました。ここで引数のhogeはgetHoge()メソッドが返す値となります。

<s:iterator id="t" value="searchResult">
 <s:if test="#t.isDisplay(hoge)">
  ここで表示する内容を編集
 </s:if>
</s:iterator>

この例のように単純な場合は、検索結果を作成する時に必要なデータだけをリストにすれば良いのですが、実際は複雑な条件で表示判定が必要な場面で発生したものです。今回は説明の為に単純化してあります。

2011年6月23日木曜日

一覧画面で入力しつつ明細にも飛びたいよ~

一覧画面を表示して、一覧標示の各行の項目をクリックすると明細の表示をするといったアプリケーションは良く見かけますが、この場合は<a>タグを使って明細表示に飛ばすようにすることが多いと思います。

今回のアプリケーションでは同じような一覧画面で各行に入力項目があるというケースです。

この場合、明細表示には<a>タグを使っているので、入力フォームのsubmitが行われないので、入力した内容がサーバ側で送信されません。明細から一覧画面に戻った時に、入力した内容が元に戻ってしまっているのが残念です。まあ、入力については明細画面で全部させれば良いという案もありますが、大量データの選択などのように単純な作業については一覧画面で行いつつ、明細を見たい場合もあるというケースもまた良くあると思います。

今回は<s:hidden>とJavaScriptを使ってsubmitさせるようにしました。

とりあえず画面には<s:hidden id="index">としてどの行が選択されたかをサーバに送る為の項目を用意します。あとは次のようなメソッドを用意して

function fncSubmit(value){
  document.forms["hogeForm"].elements["index"].value = value;
  document.forms["hogeForm"].submit();
 }

明細の表示ではonClickでこのメソッドをインデックスを引数として呼び出すようにしておきます。また、本来のsabumitボタンではインデックスを-1としてこのメソッドを呼び出すようにしてあげれば、あとはサーバ側でindexの内容を判定して、明細表示をしてあげれば良い事になります。

2011年6月21日火曜日

不正なURL直接指定-その2-

昨日は存在するアクションをパッケージを指定しないでURLで直接呼び出した時の動作を見てみましたが、そのテストの中で間違ったアクションを指定した場合についてのメモです。

結論から言いますと「IndexAction」が呼び出されます。

ということで、IndexActionを作成して、URLの直接呼び出しかを判定してエラー処理をすることにしました。

ところが最終的になぜIndexActionが呼び出されるのかは今一つ不明です。ネットで調べてみるとConvention pluginを見てみろというページを発見しましたが、Convention pluginってアノテーションを処理するプラグインのはずですが?

URLからアクションへの振り分けについてはディスパッチャフィルタが担当しているはずです。

それでもActionに関する情報はConvention pluginが持っているので、該当するURLがマッピングできなかった場合にIndexActionを返す仕様になっているのかな?

ソースを見てみればいいんでしょうが、時間もないのでいつものように結果オーライで前に進むことにしました(^^;

2011年6月20日月曜日

URLで直接アクションを起動

Webアプリケーションは通常複数のWebページを一定の順序で辿ることで一連のデータ処理を行うように設計されますが
  • ブックマークやURL直接入力によるページ呼び出し
  • 「送信」ボタン複数回クリックによるフォームデータの再送信
  • 「戻る」ボタン操作によるフォームデータの再送信
  • ページの「再読み込み」操作によるフォームデータの再送信
  • タブブラウザのセッション復元機能によるページ呼び出し
といった操作で設計とはことなる画面の流れが発生する場合があります。 Struts2では「送信」ボタン複数回クリックに対してはTokenInterceptorが用意されているので、簡単に複数回クリックを排除することができますが、URL直接入力は悪意あるユーザによるページ呼び出しの場合が多く、対策をしておく必要があります。

現在作成してアプリケーションでは対策をいろいろと施していますが、そのテストの中で以下のような問題が発生したのでメモっておくことにします。

/hoge/package/app01といったアクションを定義した場合(hogeはアプリケーション)に、通常は@Namespace("/package")で@Action(value="app01")で呼び出すことになります。 これを/hoge/app01とURLで指定した場合に、アクションとしては/hoge/package/app01が呼び出されますが、(期待値としてはエラーになって欲しかった)
  • アノテーションで定義された@InterceptorRefが処理されない。
  • アノテーションで定義された@Resultが反映されない。
といった通常とは違う動作をするようです。

ここで、表示要求に対してはCodeBehindによる画面遷移で処理されてしまいます。したがって「error」をActionの戻り値とした場合、app01-error.jspが表示されます。

とりあえず、ユーザ作成のインターセプターを利用しているので、インターセプターから呼び出されているかをアクションのexecute()で判定してエラー処理を行うことにしました。

でも、もっとスマートな判定方法はないかな?