SAStrutsはSeasarプロジェクトのWebフレームワーク。ActionやFormの扱いがStrutsとは大きく違うため、Struts上に作った別のフレームワークと考えると良いかもしれない。
バリデーションや画面遷移をアノテーションで指定出来るため、Struts特有の設定ファイルが要らずコードに集中でき書きやすい。
EclipseとDoltengプラグインを使ってSAStrutsのプロジェクトを作る。パッケージ名だけ最初に決める必要がある。
ActionはタダのJava Object (POJO) でStruts Actionクラスを継承する必要は無い。
package jp.paulownia.test.action; public class HogeAction { @Required public String id; // パラメータでnameが送られてくるとココに入っている。 @Execute(validation=false) // このアクションメソッドではバリデーションしない public String list() { return "list.jsp"; } @Execute(input="list") // このアクションメソッドでは検査する。エラー時はlistメソッドを実行する public String edit() { return "edit.jsp"; } }
というアクションがある場合、 http://example.com:8080/context_path/hoge/edit へアクセスすると
jp.paulownia.test.action.HogeAction#edit
メソッドが呼ばれる。そしてメソッドの戻り値で指定した
/WEB-INF/view/hoge/edit.jsp
のJSPを実行する。(/hoge/はアクション名から自動補完される。)
Actionの下にパッケージを作ってそこにActionを置くと、URLのパスが深くなる
http://example.com:8080/context_path/fuga/hige/edit
package jp.paulownia.test.action.fuga; public class HigeAction { @Execute(validation=false) public String edit() { return "edit.jsp"; } }
遷移先を指定する戻り値とinputの値はjsp名、またはアクションメソッド名。forward先をしてい
アクションフォームは、@ActionFormアノテーションで指定
package jp.paulownia.test.action; public class HogeAction { @ActionForm public HogeForm hogeForm; }
アクションフォームクラスは ルートパッケージ以下のformパッケージに作成。命名規則はXxxxForm。
package jp.paulownia.test.form; public class HogeForm { @Required public String name; }
注意:以下は古いやり方
入力パラメータはActionの中のpublicフィールドに入るので特にアクションフォームを作成する必要はない
public class HogeAction { public String name; // パラメータでnameが送られてくるとココに入っている。 }
セッションスコープのActionFormが必要ならDTOを作成する
package jp.paulownia.test.action; public class HogeAction { @ActionForm public HogeDto hogeDto; }
package jp.paulownia.test.action; @Component(instance = InstanceType.SESSION) public class HogeDto implements Serializable { public String name; }
public class HogeAction { @Required public String id; // パラメータでnameが送られてくるとココに入っている。 @Execute(validation=false) // このアクションメソッドではバリデーションしない public String list() { return "list.jsp"; } @Execute(input="list.jsp") // このアクションメソッドでは検査する。エラー時はlist.jspに戻る public String edit() { return "edit.jsp"; } }
適当なDTOを作ってInstanceTypeをSESSIONにして、Actionクラスにフィールドを作っておくとコンテナが自動的にインスタンスをInjectしてくれます。
@Component(instance = InstanceType.SESSION) public class FugaDto implements Serializable { private String value; ... }
アクションクラスで
@Component(instance = InstanceType.SESSION) public class FugaAction implements Serializable { @Resource private FugaDto fugaDto; ... }
FugaDtoをpublicフィールドにすると、fuga/index?fugaDto=1 みたいなURLでアクセスするとDTOを上書きできてしまう(?)ので止めた方が良いという噂。
インスタンスのライフサイクルをAPPLICATIONにするだけ、あとはSessionと同じように使う
@Component(instance = InstanceType.APPLICATION) public class PiyoDto implements Serializable { private String value; ... }
S2TestCaseではActionをテストクラスにインジェクションできないので、テストコード中でnewする。アクションの依存オブジェクト(サービスやJdbcManager)は、まずテストクラスにインジェクションして、アクションに手動でセットする。
public class HelloActionTest extends S2TestCase { public JdbcManager db; public FugaService fugaService; protected void setUp() throws Exception { include("app.dicon"); super.setUp(); } public void testIndex () { // テスト対象のアクションをnew HelloAction test = new HelloAction(); // 依存クラスは自分でセット test.db = db; test.fugaService = fugaService; assertEquals("index.vm", test.index()); assertEquals("Hello SAStruts Unit Test!", test.message); assertEquals(new SimpleDateFormat("yyyy-MM-dd").format(new Date()), test.today); }
サンプルとして、アクションメソッドを起動できるHTTPメソッドを制限するインターセプタを作成する。
という仕様。
インターセプタクラス
package your.rootpackage.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.aopalliance.intercept.MethodInvocation; import org.seasar.framework.aop.interceptors.AbstractInterceptor; import org.seasar.struts.util.RequestUtil; import org.seasar.struts.util.ResponseUtil; /** * SAStrutsのアクションメソッドの実行を、 * 特定のHTTPメソッドによるアクセスの場合のみに許可するインターセプタです。 * 許可されないHTTPメソッドでアクセスされた場合、HTTPステータス405を返します。 * */ public class HttpMethodInterceptor extends AbstractInterceptor { /** * 許可するHTTPメソッド名、カンマ区切りで複数指定可。 */ public String value; @Override public Object invoke(MethodInvocation invocation) throws Throwable { if (this.value != null) { HttpServletRequest request = RequestUtil.getRequest(); String requestMethod = request.getMethod(); String[] methods = this.value.split(","); for (String method: methods) { if (method.trim().equalsIgnoreCase(requestMethod)) { return invocation.proceed(); } } } HttpServletResponse response = ResponseUtil.getResponse(); response.setStatus(405); return null; } }
アノテーション
package your.rootpackage.interceptor; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.seasar.framework.aop.annotation.Interceptor; /** * {@link HttpMethodInterceptor}をSAStrutsのアクションメソッドに適用するアノテーションです。 * 引数で許可するHTTPメソッドを指定します。カンマ区切りで複数のHTTPメソッドを指定できます。 * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Interceptor("httpMethodInterceptor") public @interface HttpMethod { String value(); }
使い方
public class AuthAction { @Execute(input = "index") @HttpMethod("post,get") public String login() { // 何か処理 } }