コンピュータクワガタ

かっぱのかっぱによるコンピュータ関連のサイトです

独自のバリデータ(Validator)を作ってみる

SAStrutsでは独自バリデータも作れます。
Strutsで作っていたものがある場合には、それを流用することもできます。
今回は、あまりいいネタもなかったので、特定の数の倍数でないとエラーになるバリデータを作ってみました。

アノテーションを作る

まずは、アノテーションから作ります。
アノテーションから作るのは、ここでバリデータの仕様がだいたい決まるからです。

package sample.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.seasar.struts.annotation.Arg;
import org.seasar.struts.annotation.Msg;
import org.seasar.struts.annotation.Validator;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Validator("afoCheck")
public @interface AfoCheck {
    int baisu() default 3;

    Msg msg() default @Msg(key = "errors.afo");

    Arg arg0() default @Arg(key = "");

    Arg arg1() default @Arg(key = "${var:baisu}", resource = false);

    String target() default "";
}

デフォルトでは、3の倍数でない場合にはエラーとしますが、baisuに任意の値を入れることも可能です。
エラーメッセージのデフォルトは、msgアノテーションで指定します。
arg0については、デフォルトではSAStrutsのものと同じ動きで、プロパティ名を出力します。国際化する場合には、labels.プロパティ名をメッセージリソースに用意します。
arg1は、デフォルトでは、baisuに設定した値を出力します。変えることも可能です(あまり意味はないですが)。
targetは、バリデータでチェックするメソッド名をカンマ区切りで指定します。この辺もSAStrutsのほうで面倒を見てくれるので、上記のように記述しておく必要があります。

ちなみに、@Retentionとか、@Targetアノテーションは、先日の日記(http://d.hatena.ne.jp/kuwalab/20080615#1213527631)を参照。

チェックメソッド作成

チェックメソッドは、org.seasar.struts.validator.S2FieldChecksクラスを継承するのが楽です。
今回は、以下のように書きました。

package sample.util;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.struts.action.ActionMessages;
import org.seasar.struts.validator.S2FieldChecks;

public class AfoFieldChecks extends S2FieldChecks {
    private static final long serialVersionUID = 1L;

    public static boolean validateAfoCheck(Object bean, 
            ValidatorAction validatorAction,
            Field field, ActionMessages errors,
            Validator validator, HttpServletRequest request) {
        String value = getValueAsString(bean, field);
        if (!GenericValidator.isBlankOrNull(value)) {
            try {
                int checkValue = Integer.parseInt(value);
                int baisu = Integer.parseInt(field.getVarValue("baisu"));
                if (checkValue % baisu != 0) {
                    addError(errors, field, validator, validatorAction, request);
                    return false;
                }
            } catch (Exception e) {
                addError(errors, field, validator, validatorAction, request);
                return false;
            }
        }
        return true;
    }
}

ソースは簡単なので、解説の必要はあまりないかと思います。

validator-rules.xml

最後に、WEB-INF以下のvalidator-rules.xmlファイルに追記します。

<validator name="afoCheck"
      classname="sample.util.AfoFieldChecks"
         method="validateAfoCheck"
   methodParams="java.lang.Object,
                 org.apache.commons.validator.ValidatorAction,
                 org.apache.commons.validator.Field,
                 org.apache.struts.action.ActionMessages,
                 org.apache.commons.validator.Validator,
                 javax.servlet.http.HttpServletRequest"
         depends=""
            msg="errors.afo"/>

nameにバリデータの名前、classnameにチェックするクラス、methodにクラスのメソッド名、msgにメッセージのキーを指定します。
あとは、このチェックをする前にチェックする必要のあるバリデータがあれば、dependsに指定します。(longRangeのチェックの場合には、longとかを指定している。)

メッセージリソース

メッセージと、プロパティを国際化する場合に記述します。
今回は以下のようにしています。

errors.afo={0}は{1}の倍数でなければ、アフォになります。
labels.afo1=項目「アフォ1」

使ってみる

最後にActionで使ってみます。
まずは、JSP
index.jspとしています。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
 <head>
  <title>アフォチェック</title>
 </head>
 <body>
  <html:errors/>
  <s:form>
 好きな数字を入れてよ <html:text property="afo1" size="10" maxlength="10" /><br />
 好きな数字を入れてよ <html:text property="afo2" size="10" maxlength="10" /><br />
   <input type="submit" name="afoCheck1" value="1と2"/>
   <input type="submit" name="afoCheck2" value="1"/>
   <input type="submit" name="afoCheck3" value="2"/>
  </s:form>
 </body>
</html>

そして、Action。

package sample.action;

import org.seasar.struts.annotation.Execute;

import sample.annotation.AfoCheck;

public class AfoAction {
    @AfoCheck(target = "afoCheck1, afoCheck2")
    public String afo1;

    @AfoCheck(baisu = 4, target = "afoCheck1, afoCheck3")
    public String afo2;

    @Execute(validator = false)
    public String index() {
        return "index.jsp";
    }

    @Execute(input = "index.jsp")
    public String afoCheck1() {
        return "index.jsp";
    }

    @Execute(input = "index.jsp")
    public String afoCheck2() {
        return "index.jsp";
    }

    @Execute(input = "index.jsp")
    public String afoCheck3() {
        return "index.jsp";
    }
}

動かしてみる

無事、アフォになりました。

感想

思ったより簡単にできました。
バリデータをXMLで書いていた頃を思うと、アノテーションでエラーチェックができるのはなんて簡単だろうと思います。
Strutsで独自バリデータを作っている人はかなりいる(と思う)ので、SAStrutsでもちょっとアノテーションを作成するだけで、資産を流用できるのはいいと思います。