2002/2/15 作成
2003/8/8 更新
Struts1.1の新機能について見ていこうと思います。
なお、このあたりは、Jakarata本(Jakarataプロジェクト徹底攻略)
をかなり参考にしました。
Action#execute()
Actionクラスの実行メソッド、 Action#perform()がdeplecatedになり、 Action#execute()に変更になりました。
public ActionForward perform(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
...
}
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
...
}
何が変わったというと、太字のスローする例外の部分です。
これまで、ServletExceptionにラッピングして投げていたような例外を
そのままスローできるようになりました。
システムエラーに近いSQLExceptionやRemoteExceptionなどの
チェック例外もそのままスローできます。
struts-config.xmlとApplicationResource.propertiesに 例外の種類とメッセージ、遷移先を記述することで、 汎用的なエラー画面を作成できます。
struts-config.xml
<global-exceptions>
<exception
key="error.sql"
type="java.sql.SQLException"
path="/error.jsp"/>
<exception
key="error.remote"
type="java.rmi.RemoteException"
path="/error.jsp"/>
<exception
key="error.any"
type="java.lang.Exception"
path="/error.jsp"/>
</global-exceptions>
ApplicationResource.properteis
error.sql=データベースのエラー error.remote=通信エラー error.any=何だかエラー
web.xmlのexception要素でも似たようなことができますが、 ActionServlet側で制御するのと、Servlet側でチェック例外 が投げれるところがちょっと違うようです。
Struts1.1より前では、自前でActionServletの継承クラスやActionの基底クラスを作って、 汎用例外の共通処理を書いてた処理をフレームワーク側の設定ファイルで 書けるようになった、という感じです。
なお、Action#excute()でExceptionを投げているからと言って、
その先のビジネスロジックのメソッドで何でもかんでも
throws Exceptionするのはやめといた方がいいでしょう
(せっかくの例外チェック機構の恩恵が半減するので)。
Validator
これまで、ActionForm#validate()に書いていた入力チェックを 入力チェック用のxmlに記述するとこで、 汎用的で変更しやすい入力チェックが可能になります。
最初、複雑で何をどうするんじゃ〜という印象だったんですが、 簡単なチェックは以外とすんなりできました。行うことは以下です。
import org.apache.struts.validator.ValidatorForm;
public class BattleForm extends ValidatorForm {
private String name;
private String word;
//validateメソッドの内容をxmlに移動
// public ActionErrors validate(ActionMapping mapping,
// HttpServletRequest request) {
// ...
// }
...
}
struts-config.xml(1.0.2と同じ)
<form-bean name="battleForm"
type="wb.BattleForm"/>
validator.xml
<formset>
<form name="battleForm">
<field
property="name"
depends="required,minlength">
<arg0 key="battleForm.name"/>
</field>
<field
property="word"
depends="required">
<arg0 key="battleForm.word"/>
</field>
</form>
</formset>
ApplicationResources_ja.properties(1.0.2と同じ)
errors.required={0} を入力してください.
ここまででValidatorを使ったサーバーサイドの入力チェックが可能です。
JSP
<html:errors/> <%--<html:form action="/battle">--%> <html:javascript formName="battleForm"/> <html:form action="/battle" onsubmit="return validateBattleForm(this)"> ... </html:form>
なお、validateBattleFormは自動的に作られるJavaScript関数名です。 クライアント側の入力チェックを自動生成でき、しかも、 サーバーサイドと入力チェックと一貫性を保てるのは、興味いですね。
Struts付属サンプルのstruts-validator.warを見ると、
必須チェックの他、
文字数チェック、正規表現を使った妥当性チェックなど、
様々なチェックの例があります。
DynaActionForm
Struts1.1より前は必ず、Formからアクションを呼ぶ場合、 必ずActionFormクラスを継承してクラスを作らなければなりませんでした。 この手間を省くために(あるいは別のアプローチとして?) DynaBeanFormが提供されました。
DynaActionFormの利用は簡単です。
struts-config.xml
<!--普通のActionForm-->
<!--
<form-bean name="battleForm"
type="wb.BattleForm2"/>
-->
<!--DynaActionForm-->
<form-bean name="battleForm"
type="org.apache.struts.action.DynaActionForm">
<form-property
name="name"
type="java.lang.String"/>
<form-property
name="word"
type="java.lang.String"/>
</form-bean>
Actionクラス
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// BattleForm battleForm = (BattleForm)form;
// String name = battleForm.getName();
// String word = battleForm.getWord();
DynaBean battleForm = (DynaBean)form;
String name = (String)battleForm.get("name");
String word = (String)battleForm.get("word");
...
}
DynaBeanではプロパティーをキーにしてgetします。 validatorを組み合わせて、かなり便利だと思われます。 なんとなく、 request.getParameter("name")的なMapのアプローチに戻ってる ような気がするのは私だけでしょうか。。。
普通のActionFormとDynaActionFormの使い分けがちょっと問題ですね。
恐らく、単純なものや、Actionクラスから後ろのビジネスロジックに、
Formをそのまま渡さないような場合、DynaActionFormを使うといいかな?
という気がします。あるいは設計方針によっては、全部、DynaActionFormというのもアリかもしれません。
PlugIn
Webアプリの初期化、終了処理を記述したPlugInクラスを 定義できるようになりました。 以下の手順で組み込みます。
PlugInインターフェイスを実装したクラスを作成
package wb;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
public class WordBattlerPlugin implements PlugIn{
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
ServletContext context = servlet.getServletContext();
context.setAttribute("BATTLE_MANAGER",new BattleManager());
}
public void destroy() {
}
}
struts-config.xmlにplugin記述
<plug-in className="wb.WordBattlerPlugin"/>
上の例だと、web.xmlでlistenerを定義しても同じことができますが、 Strutsに依存した処理(ActionServletから情報をとってどうこうとか?) の場合は、PlugInが使えそうです。
PlugInだけに付けたり取ったりが簡単なのか。。。
ValidatorやTilesもPluginが使われているようです。
NestedTag
NestedTagは、Strutsカスタムタグのネストをサポートします。nestedタグの内部では、name要素が省略できます。
NestedTag使用前
<logic:iterate id="word" name="BATTLE_MANAGER" property="words">
<tr>
<td><bean:write name="word" property="name"/>
<td><bean:write name="word" property="word"/>
<td><bean:write name="word" property="score"/>
</tr>
</logic:iterate>
NestedTag使用後
<nested root="BATTLE_MANAGER">
<nested:iterate property="words">
<tr>
<td><nested:write property="name"/>
<td><nested:write property="word"/>
<td><nested:write property="score"/>
</tr>
</nested:iterate>
</nested:root>
ほとんどのStrutsタグに対してnested:XXXが提供されています。これは、使えますね。なお、html:formの中ではフォーム名をネストルート名として使えますが、フォーム以外で使う場合はnested:rootでネストのルートを指定する必要があります。
適当な例かわかりませんが、nestedタグを使うと、以下のようなテーブル形式の入力画面も簡単に実現できます。
<html:form action="hoge">
<html:submit/>
<table>
<nested:iterate
property="hogeBeanList"
indexId="index">
<tr>
<td>
<bean:write name="index"/>
</td>
<td>
<nested:text property="name"/>
</td>
<td>
<nested:text property="age"/>
</td>
</tr>
</nested:iterate>
</table>
</html:form>
Formにmethod=getをつけると、 ....&hogeBeanList[3].name=no1&... というような形でリクエストが送られているのがわかります。 この形でリクエストパラメータが送られるので、HogeForm#getHogeForm().setName("no1")とフォームに値がセットされるわけです。
次ページ、前ページなどのページめくり機能がある場面でも、Form(Action)をセッションスコープ(デフォルトでセッションスコープ)にしておけば、iterateのoffsetとlengthを使って、ページをまたいで、Formに正しく値をセットすることができます。
wb-0.3
そしてバージョンアップされたワードバトラーがwb-0.3.zip
(warじゃないです。容量がでかいのでlibの中のjarを消してます。struts-blank.warに上書きして実行できます)。
あんまり変わってないですが、
簡単にValidator、Plugin、NestedTagを組み込みました。