チュートリアル
Seasar2はHOT deployに対応しているので、ソースコードの変更を アプリケーションサーバを再起動することなく認識できます。 また、設定ファイルもほとんど書く必要がありません。
チュートリアルのセットアップに従って、 アプリケーションサーバを起動したら、Javaのコードや*.propertiesファイルをいろいろ 書き換えながら、HOT deployの威力を実感してください。
index
ブラウザでhttp://localhost:8080/sa-struts-tutorial/にアクセスしてください。 いろんなアプリケーションを試せるページが表示されましたね。
webapp(Webアプリケーションのルート)の直下を見てみましょう。index.jsp(index.html)は存在しません。 それでは、どのページが表示されているのでしょうか。
SAStrutsは、Webアプリケーションのルートにアクセスすると、 ルートパッケージ.action.IndexActionというクラスがあれば、 自動的にそのアクションに遷移します。 ルートパッケージの詳細は、こちらを参照ください。
IndexAction.javaのソースコードは次のようになります。
IndexAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; public class IndexAction { @Execute(validator = false) public String index() { return "index.jsp"; } }
アクションに遷移した後、@Executeのついた実行メソッドが呼び出されます。 実行メソッドを複数定義した場合に、どの実行メソッドが呼び出されるのかは、 こちらを参照してください。 今回は、実行メソッドが1つしかないので、無条件にindex()が呼び出されます。
メソッドの戻り値は遷移先になります。 IndexAction#index()の場合は、index.jspに遷移します。 web.xmlでVIEW_PREFIXが/WEB-INF/viewと指定されているので、 具体的な遷移先は、/WEB-INF/view/index.jspになります。
それでは、webapp/WEB-INF/struts-config.xmlを見てみましょう。 アクションやアクションフォームの設定はまったくありません。 SAStrutsは、URLに結びつく適切なアクションクラスを探し出し、 そのアクションクラスの情報を元に、struts-config.xmlに相当する情報を組み立てています。
webapp/WEB-INF/validation.xmlも見てみましょう。 あら、そんなファイルないですね。 SAStrutsは、検証用のアノテーションをプロパティに設定することで、 validation.xmlに相当する情報を組み立てています。
足し算
インデックスページから足し算のページにアクセスしてください。 あるいは、ブラウザでhttp://localhost:8080/sa-struts-tutorial/add/にアクセスしても同じです。 足し算のページが表示されましたね。
/addに対応するアクションクラスは、tutorial.action.AddActionです。 アクションの詳細は、こちらを参照してください。
AddAction.javaのソースコードは次のようになります。
AddAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.IntegerType; import org.seasar.struts.annotation.Required; public class AddAction { @Required @IntegerType public String arg1; @Required @IntegerType public String arg2; public Integer result; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(input = "index.jsp") public String submit() { result = Integer.valueOf(arg1) + Integer.valueOf(arg2); return "index.jsp"; } }
アクションはPOJO(特定のクラスを継承しない普通のJavaのクラス)で記述し、 アクションの状態もアクションに定義します。 関連する情報は同じクラスにあったほうがわかりやすいからです。
Seasar2はpublicフィールドをプロパティとみなすことができるので、 getter,setterは記述する必要はありません。 publicフィールドは、ELやStrutsからでも認識できるようになっています。 そのからくりに興味のある方は、org.seasar.struts.actionのソースを眺めてください。
リクエストに対する処理は、実行メソッドに記述します。 実行メソッドには、@Executeをつけます。 実行メソッドの戻り値は、遷移先のパスになります。
実行メソッドが複数ある場合、どの実行メソッドが選択されるのかは、URLで指定するか、 リクエストのパラメータのキーにメソッド名が含まれている(値が1文字以上あること)かどうかで決まります。
次の例では、AddAction#index()が呼び出されます。 /add/の後ろがメソッド名になります。
http://localhost:8080/sa-struts-tutorial/add/index
メソッドが定義されていない場合、index()が呼び出されるので、 上記の呼び出しは、下記の呼び出しと一緒です。
http://localhost:8080/sa-struts-tutorial/add/
フォームをサブミットする場合は、ボタン系のname属性に実行メソッド名を指定します。 次の例では、アクションクラスのsubmit()を呼び出します。
<input type="submit" name="submit" value="サブミット"/>
@Executeの詳細は、こちらを参照してください。
アノテーション(バリデータ)による検証を行なうには、 フィールドに検証用のアノテーションをつけ、 @Executeのvaliator属性をtrue(デフォルト)にします。 validator=trueの場合は、検証結果がNGのときの遷移先をinput属性で指定する必要があります。 バリデータの詳細は、こちらを参照してください。
/add/index.jspは次のようになります。
/add/index.jsp
<html> <head> <title>Add</title> </head> <body> <html:errors/> <s:form> <html:text property="arg1"/> + <html:text property="arg2"/> = ${f:h(result)}<br /> <input type="submit" name="submit" value="サブミット"/> </s:form> </body> </html>
どのJSPでも共通に必要になるtaglibの宣言は、webapp/WEB-INF/view/common/common.jspで定義されています。 common.jspの詳細は、JSPを参照してください。
検証結果がNGのときのメッセージを表示させるために、 html:errorsタグを定義します。
サブミットされた値を受け取るためにs:formを定義します。 action属性は自動的に計算されるので通常は指定する必要がありません
入力値を受け取るために、html:textタグを定義します。 property属性にアクションのプロパティ名を指定します。
サブミット時にAddAction#submit()を呼び出すために、サブミットタグの name属性の値をsubmitに設定します。
サブミットされると、入力したarg1、arg2の値がアクションに設定され、 バリデータにより必須かどうか、整数かどうかが検証されます。
バリデータの結果がNGだった場合は、input属性で指定したindex.jspに戻されます。 バリデータの結果がOKの場合は、submit()が呼び出されます。
submit()の戻り値がindex.jspなので、またindex.jspに遷移します。
アクションのresultプロパティは、リクエストの属性に同じ名前でセットされているので、 ${f:h(result)}のようにして表示できます。 f:hはHTMLをエスケープするファンクションです。 詳細は、こちらを参照してください。
ファイルアップロード
インデックスページからファイルアップロードのページにアクセスしてください。 あるいは、ブラウザでhttp://localhost:8080/sa-struts-tutorial/uploadにアクセスしても同じです。 ファイルアップロードのページが表示されましたね。
/uploadに対応するアクションクラスは、tutorial.action.UploadActionです。 アクションの詳細は、こちらを参照してください。
UploadAction.javaのソースコードは次のようになります。
UploadAction.java
package tutorial.action; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.upload.FormFile; import org.seasar.framework.container.annotation.tiger.Binding; import org.seasar.framework.container.annotation.tiger.BindingType; import org.seasar.framework.exception.IORuntimeException; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; import org.seasar.struts.upload.S2MultipartRequestHandler; import org.seasar.struts.util.ActionMessagesUtil; public class UploadAction { @Required @Binding(bindingType = BindingType.NONE) public FormFile formFile; @Binding(bindingType = BindingType.NONE) public FormFile[] formFiles; public HttpServletRequest request; public ServletContext application; @Execute(validator = false) public String index() { SizeLimitExceededException e = (SizeLimitExceededException) request .getAttribute(S2MultipartRequestHandler.SIZE_EXCEPTION_KEY); if (e != null) { ActionMessages errors = new ActionMessages(); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "errors.upload.size", new Object[] { e.getPermittedSize(), e.getActualSize() })); ActionMessagesUtil.addErrors(request, errors); } return "index.jsp"; } @Execute(input = "index.jsp") public String upload() { ActionMessages messages = new ActionMessages(); upload(formFile, messages); for (FormFile file : formFiles) { upload(file, messages); } ActionMessagesUtil.addMessages(request, messages); return "index.jsp"; } protected void upload(FormFile file, ActionMessages messages) { if (file.getFileSize() == 0) { return; } String path = application.getRealPath("/WEB-INF/work/" + file.getFileName()); try { OutputStream out = new BufferedOutputStream(new FileOutputStream( path)); try { out.write(file.getFileData(), 0, file.getFileSize()); } finally { out.close(); } } catch (IOException e) { throw new IORuntimeException(e); } messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "messages.upload.complete", new Object[] { path })); } }
ファイルアップロードは、FormFileを型にしたプロパティをActionまたはActionFormに定義し、 FormFile経由でアップロードされたファイルを取得します。
FormFileはインタフェースであるため、Seasar2では 自動バインディング の対象とみなされ、実行時にDIされない旨の警告が出てしまいます。 これを防ぐためにプロパティには、@Binding(bindingType = BindingType.NONE)をつける必要があります。
プロパティの型をFormFile[]のように配列指定することで、 複数のFormFileを受け取ることができます。
FormFileを配列で指定したときには、 バリデーション用のアノテーションを指定することができません。 実際にアップロードされたかどうかは、FormFile#getFileSize()が0を返すかどうかで判断してください。
アップロードされたファイルのサイズが、 あらかじめ決められた上限(struts-config.xmlのcontrollerタグのmaxFileSize属性の値) を超えた場合、SizeLimitExceededExceptionがスローされます。
この例外が発生した場合は、indexメソッドに制御が移るので、 indexメソッドで、SizeLimitExceededExceptionの存在をチェックすることで、 アップロードされたファイルが上限を超えたのかチェックすることができます。
/upload/index.jspは次のようになります。
/upload/index.jsp
<html> <head> <title>Upload</title> </head> <body> <html:errors/> <html:messages id="m" message="true"> ${f:h(m)}<br /> </html:messages> <s:form action="/upload" enctype="multipart/form-data"> <input type="file" name="formFile" /><br /> <c:forEach varStatus="s" begin="0" end="1"> <input type="file" name="formFiles[${s.index}]" /><br /> </c:forEach> <input type="submit" name="upload" value="アップロード"/> </s:form> </body> </html>
ファイルをアップロードする場合は、formのenctype属性を"multipart/form-data"にします。 アップロード用のタグ(<input type="file" ... />)のname属性は、 ActionまたはActionFormのプロパティ名にあわせます。 FormFileを配列で受け取る場合は、name属性の値をプロパティ名[配列のインデックス]にします。
クライアントサイドバリデータ
インデックスページからクライアントサイドバリデータのページにアクセスしてください。 あるいは、ブラウザでhttp://localhost:8080/sa-struts-tutorial/clientValidator/にアクセスしても同じです。 クライアントサイドバリデータのページが表示されましたね。
何も入力していない状態で「aaaが必須」のボタンをクリックしてください。 JavaScriptで検証が行なわれて「aaaは必須です。」のメッセージが表示されました。
今度は同様に「bbbが必須」のボタンをクリックしてください。 JavaScriptで検証が行なわれて「bbbは必須です。」のメッセージが表示されました。 それぞれのボタンに対して異なる検証が行なわれていることがわかります。
/clientValidatorに対応するアクションクラスは、tutorial.action.ClientValidatorActionです。 アクションの詳細は、こちらを参照してください。
ClientValidatorAction.javaのソースコードは次のようになります。
ClientValidatorAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; public class ClientValidatorAction { @Required(target = "submit") public String aaa; @Required(target = "submit2") public String bbb; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit2() { return "clientValidator.jsp"; } }
ボタン(メソッド)ごとに対象となるバリデータを変えたい場合、 バリデータ用のアノテーションのtarget属性にメソッドの名前を指定します。 複数のメソッドを指定したい場合は、カンマ(,)で区切ってください。
/clientValidator/index.jspは次のようになります。
/clientValidator/index.jsp
<html> <head> <title>Client Validator</title> <html:javascript formName="clientValidatorActionForm_submit"/> <html:javascript formName="clientValidatorActionForm_submit2"/> </head> <body> <html:errors/> <s:form action="/clientValidator"> aaa:<html:text property="aaa"/><br /> bbb:<html:text property="bbb"/><br /> <input type="submit" name="submit" value="aaaが必須" onclick="forms[0].name='clientValidatorActionForm_submit'; return validateClientValidatorActionForm_submit(forms[0]);"/> <input type="submit" name="submit2" value="bbbが必須" onclick="forms[0].name='clientValidatorActionForm_submit2'; return validateClientValidatorActionForm_submit2(forms[0]);"/> </s:form> </body> </html>
検証用のJavaScriptを出力するために、html:javascriptタグを使います。 formName属性には、「アクション名 + Form + _ + メソッド名」を指定します。
検証用のJavaScriptを呼び出すために、ボタン系タグのonclick属性で、 form要素のname属性に上記のformName属性の値を代入した後に、 「validate + formName属性の値」のメソッドを呼び出します。