自作バリデータの作り方

前回はJavaアプリケーションのためのバリデーションフレームワークである「JaValid」の基本的な使い方を紹介した。JaValidではあらかじめ用意されたバリデータ以外にオリジナルのバリデータを定義して使用することもできる。バリデータの定義から利用までの手順は次のような具合になる。

  1. アノテーションを定義する
  2. そのアノテーションのためのバリデータを定義する
  3. 設定ファイルを作成する
  4. AnnotationValidatorを生成する際に、作成した設定ファイルを指定する

本稿では次のようなバリデータを作成する一連の流れを通して、バリデータを自作する方法を解説したい。

  • 文字列が正しいEメール形式になっているかどうかを検証する
  • 許可しないドメインを指定できる

まず、バリデーション条件の指定に使用するためのアノテーションを定義するこれは通常のアノテーションを作成する場合と同様に行えばいい。ここでは次のように定義した。

リスト1

package jp.co.mycom.toolde;

import java.lang.annotation.*;
import org.javalid.annotations.core.JvGroup;

@Target (value={ElementType.METHOD,ElementType.FIELD})
@Retention (RetentionPolicy.RUNTIME)
@Documented
public @interface EmailString {
    String[] exceptDomains() default {};
    String[] applyToGroups() default { JvGroup.GROUP_APPLY_ALL };
}

アノテーション名は@EmailStringで、許可しないドメインをexceptDomainsプロパティとして指定できるようになっている。applyToGroupsはグループを指定できるようにする場合に必要なプロパティで、ここでは全てのグループに所属できる設定にしてある。

続いて、このアノテーションのためのバリデータを行うクラスを定義する。JaValidのバリデータはorg.javalid.core.validator.JavalidValidatorインタフェースを実装したクラスとして定義する。このインタフェースには空の実装としてAbstractJavalidValidatorImplクラスが用意されているので、これを継承する形で作成すると便利だ。ジェネリクスのタイプ引数には作成したアノテーションのクラス(今回はEmailString)を指定する。ここではEmailStringValidatorというクラス名で次のように定義した。

リスト2

package jp.co.mycom.toolde;

import java.util.*;
import org.javalid.core.*;
import org.javalid.core.validator.*;

public class EmailStringValidator extends AbstractJavalidValidatorImpl {
    @Override
    public List<ValidationMessage> doValidate(EmailString annotation,
                Object value, String path, JvConfigurationWrapper config) {
        // 文字列以外の場合は例外を投げる
        String strValue = null;
        if(value instanceof String) {
            strValue = ((String)value).trim();
        }
        else {
            throw new JavalidException("Not of type java.lang.String");
        }

        // バリデーションの実施
        List<ValidationMessage> list = new ArrayList<ValidationMessage>();
        // メールアドレスのフォーマットになっているか
        if (!strValue.matches("[\\w\\.\\-]+@(?:[\\w\\-]+\\.)+[\\w\\-]+")) {
            String messageCode = "jv_error_emailString";
            ValidationMessage message = new ValidationMessage(
                    path,
                    messageCode,
                    new Object[] { value },
                    false);
            list.add(message);
        }
        // 除外指定されているドメインではないか
        String[] exceptDomains = annotation.exceptDomains();
        for (String domain: exceptDomains) {
            if (strValue.endsWith(domain)) {
                String messageCode = "jv_error_emailString";
                ValidationMessage message = new ValidationMessage(
                        path,
                        messageCode,
                        new Object[] { value, domain },
                        false);
                list.add(message);
            }
        }
        return list;
    }
}

AbstractJavalidValidatorImplクラスにはdoValidate()というabstractメソッドが宣言されており、この中にバリデーションのための処理を実装すればよい。検証の対象となるオブジェクトは第2引数に渡されるので、今回の例ではこれをStringにキャストした上で、正規表現でEメールのフォーマットになっているかどうかをチェックしている。また、exceptDomainsに指定されたドメインでないかはendsWith()メソッドでチェックする。もし検証に引っかかった場合にはValidationMessageオブジェクトを作成し、それをListに格納して戻り値として返す。

なおJavalidValidatorインタフェースを直接implementsする場合には、doValidate()ではなくvalidate()メソッドを同様の方法で実装する。

アノテーションとバリデータの関連付けはXML形式の設定ファイルで行う。設定ファイルの雛型は公式サイトで配布されているソースコード版の、javalid-core/src/org/javalid/core/config/ディレクトリに格納されている。たとえば今回のようにメソッドに対して指定するバリデータの場合にはjv-config-method.xmlを参考にするとよい。ここでは「my_javalid_config.xml」というファイル名でリスト3のように記述した。

リスト3

<?xml version="1.0" encoding="utf-8" ?>
<jv annotations-on-element="method">

  <settings>
    <property name="resolve-messages" value="false" />
  </settings>

  <!-- User specific registered annotations and their respective implementations -->
  <annotations>
    <annotation annotation-class="jp.co.mycom.toolde.EmailString"
                validator-class="jp.co.mycom.toolde.EmailStringValidator"
                supports-plural="false" />
  </annotations>

</jv>

<annotation>要素が該当する設定の箇所で、annotation-class属性にアノテーションのクラスを、validator-class属性にバリデータのクラスを指定する。supports-plural属性はPluralアノテーションと呼ばれる種類のアノテーションをサポートするかどうかを表すもので、今回はfalseにしてある。

以上でバリデータ側の準備は完了だ。続いて、これをテストするコードとしてリスト4のようにEmailAddressクラスを用意した。この例ではgetAddress()メソッドに、@NotNullと今作成した@EmailStringを設定している。exceptDomainsには".net"を指定した。

リスト4

package jp.co.mycom.toolde;

import org.javalid.annotations.core.ValidateDefinition;
import org.javalid.annotations.validation.NotNull;

@ValidateDefinition
public class EmailAddress {
    private String address;

    public EmailAddress(String address) {
        this.address = address;
    }

    @NotNull
    @EmailString(exceptDomains=".net")
    public String getAddress() {
        return this.address;
    }
}

バリデーションを実行するコードはリスト5のようになる。AnnotationValidatorImplクラスのコンストラクタを呼び出す部分がポイントで、このときに先ほど記述した設定ファイルを指定する。これによって作成したバリデータが有効になる。なお、前回示したコードと同様なので省略する。

リスト5

package jp.co.mycom.toolde;

import java.util.*;
import org.javalid.core.*;

public class EmailStringValidationTest {
    public EmailStringValidationTest() {
        AnnotationValidator validator = null;
        List<ValidationMessage> messages = null;
        EmailAddress email = null;

        // バリデーターの生成
        validator = new AnnotationValidatorImpl("my_javalid_config.xml");

        // [1] エラー無し
        email = new EmailAddress("takaaki@example.jp");
        messages = validator.validateObject(email);
        printErrorMessages(messages);

        // [2] アドレスがnull
        email = new EmailAddress(null);
        messages = validator.validateObject(email);
        printErrorMessages(messages);

        // [3] アドレスが正しいフォーマットになっていない
        email = new EmailAddress("takaaki.jp");
        messages = validator.validateObject(email);
        printErrorMessages(messages);

        // [4] アドレスがexceptDomainsに指定されたドメイン
        email = new EmailAddress("takaaki@example.net");
        messages = validator.validateObject(email);
        printErrorMessages(messages);
    }

    private void printErrorMessages(List messages) {
        // 省略
    }

    public static void main(String[] args) {
        EmailStringValidationTest doit = new EmailStringValidationTest();
    }
}

このコードの出力結果はプロンプト1のようになり、[3]および[4]の部分においてそれぞれ@EmailStringによるバリデーションでエラーが出ることが確認できる。

プロンプト1

No validation error.

Vallidation error from: address
    Message: jv_error_valueIsNull
    Values:  null

Vallidation error from: address
    Message: jv_error_emailString
    Values:  [takaaki.jp]

Vallidation error from: address
    Message: jv_error_emailString
    Values:  [takaaki@example.net, .net]

JaValidにあらかじめ用意されたアノテーションだけでも基本的なバリデーションの用途としては十分な威力を発揮できるが、このように必要なバリデータを自作することでその活用範囲を大きく広げられる。アーキテクチャ自身は極めてシンプルなので容易に使いこなすことができるだろう。