cypher256's blog

Pleiades とか作った

@Reset アノテーション

フォームをセッションで持つと、検索画面や変更画面にあるチェックボックスなどの値は画面でチェックをはずすと、前の値が残ってしまうので、リクエストの値がフォームにセットされる前にクリアする必要があります。SAStruts にも Struts と同じようにフォームに reset メソッドを作成することで回避可能ですが、1 機能 1 アクションにすると、すべてのアクションメソッドでリセットされてしまいます。

@Reset アノテーションを作ることにより、アクションメソッドごとにリセットメソッドを指定できます。こんな感じ。

// リセット・アノテーション
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Reset {
    String method() default "reset";
}

今回、属性名を method としましたが、アノテーション仕様により、属性が 1 つの場合は属性名を value とすることで、使うときに @Reset("resetSearch") のように method = のような記述を省略することができます。ただ、この場合、属性を増やすときに使用部分も修正が必要になってしまうので注意が必要です。

// アクション・クラス
public class HogeAction {
    // 検索
    @Execute(・・・)
    @Reset(method = "resetSearch")
    public String search() {
    }
    // 変更画面表示
    @Execute(・・・)
    @Reset(method = "resetDetail")
    public String detail() {
    }
}

最初はフォーム・クラスのリセット・メソッドに @Reset アノテーションを付けて、属性でアクション・メソッドを指定しようと思ったのですが、なんかしっくりこないので、アクションの実行メソッドに付けたほうが良いと思います。

// フォーム・クラス
@Component(instance = InstanceType.SESSION)
public class HogeDto implements Serializable {
    // 検索時のリセット
    public void resetSearch() {
        xxx = null;
    }
    // 変更画面表示時のリセット
    public void resetDetail() {
        yyy = null;
    }
}


あとは、@Reset アノテーションを処理するためにリクエスト・プロセッサの processPopulate メソッドを拡張します。SAStruts でも Struts でもこのメソッドは reset メソッドがあれば、それを呼び出し、その後リクエストをフォームにコピーします。そこで、ActionFormWrapper (SAStruts でのフォームの実体) を無名インナークラスで拡張し、reset メソッドの中で @Reset アノテーションで指定された Dto のリセットメソッドを呼び出すようにしておきます。この無名クラスのインスタンスを引数にして super で親の processPopulate メソッドを呼び出せば、無名クラスの reset が呼び出され、@Reset で指定したのメソッドが呼び出されることになります。

// リクエスト・プロセッサの拡張クラス
public class HogeRequestProcessor extends S2RequestProcessor {

    @Override
    protected void processPopulate(HttpServletRequest request,
            HttpServletResponse response, ActionForm form, ActionMapping mapping)
            throws ServletException {

        if (form == null) {
            return;
        }
        S2ExecuteConfig executeConfig = ((S2ActionMapping) mapping)
            .findExecuteConfig(request);

        if (executeConfig != null) {

            Method actionMethod = executeConfig.getMethod();
            Reset resetAnno = actionMethod.getAnnotation(Reset.class);
            final String resetMethodName = (resetAnno == null) ? null
                    : resetAnno.method();

            ActionFormWrapper formWrapper = (ActionFormWrapper) form;
            ActionFormWrapperClass formWrapperClass = (ActionFormWrapperClass) formWrapper
                .getDynaClass();

            form = new ActionFormWrapper(formWrapperClass) {
                @Override
                public void reset(ActionMapping mapping,
                        HttpServletRequest request) {
                    if (resetMethodName == null) {
                        return;
                    }
                    try {
                        Method resetMethod = actionForm.getClass().getMethod(
                            resetMethodName);
                        MethodUtil.invoke(resetMethod, actionForm, null);
                    } catch (NoSuchMethodException e) {
                        NoSuchMethodException ne = new NoSuchMethodException(
                            "@Reset アノテーションの属性 method に指定されたメソッドが見つかりません。" + e);
                        throw new NoSuchMethodRuntimeException(actionForm
                            .getClass(), resetMethodName, null, ne);
                    }
                }
            };
        }
        super.processPopulate(request, response, form, mapping);
    }
}