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を組み込みました。