BeanValidationの組み込みアノテーション

【前提条件】

[環境]
[参考資料]

Bean Validation1.1仕様書
The Java Community Process(SM) Program - communityprocess - final

[その他]

Validatorの生成はCDIを利用しています。

【概要】

Bean Validationで標準で用意されている
Validation用のアノテーションについてです。

Bean Validationの参照実装はHibernate Validator5系となっているため、
Hibernate Validatorと同様のアノテーションがあります。

【Validatorオブジェクトの生成】

Bean Validationを行うためには
javax.validation.Validatorクラスのオブジェクトを使用します。

Validatorクラスの生成はCDIを利用しない方法とCDIを利用する方法があります。

[CDIを利用しない方法]
    final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    validator = factory.getValidator();

最初にjavax.validation.ValidationのbuildDefaultValidatorFactoryメソッドを使用し、
javax.validation.ValidatorFactoryクラスのオブジェクトを生成します。

ValidatorFactory#getValidatorメソッドを呼び出すと
Validatorクラスのオブジェクトが生成されます。

[CDIを利用する方法]
    @Inject
    private Validator validator = null;

CDIを使用する場合はValidatorクラスのオブジェクトにたいして
Injectアノテーションを付与するだけです。

【Validationアノテーション

サンプルでは標準で用意されているアノテーションを使用します。
Validation用のアノテーションはjavax.validation.constraintsパッケージに配置されています。

JAX-RSのパラメータBeanとして利用するため
FormParamアノテーションを使用しています。

アノテーションにはメッセージを設定することができますが、
今回のサンプルにはメッセージを設定していません。

import java.util.Date;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import javax.ws.rs.FormParam;

public class ValidationBaseBean {

    @DecimalMax("100")
    @DecimalMin("5")
    @FormParam("decimalValue")
    private Integer decimalValue = null;

    @Max(20)
    @Min(10)
    @FormParam("maxMinValue")
    private Integer maxMinValue = null;
  
    @Size(min = 5, max = 10)
    @FormParam("sizeValue")
    private String sizeValue = null;

    @Digits(integer = 3, fraction = 2)
    @FormParam("digitValue")
    private Double digitValue = null;

    @Future
    private Date futureDate = null;

    @FormParam("futureDate")
    private String futureDateValue = null;

    @Past
    private Date pastDate = null;

    @FormParam("pastDate")
    private String pastDateValue = null;

    // アクセサメソッドは省略
}

DecimalMaxアノテーションは数値の最大値、DecimalMinアノテーションは数値の最小値を指定します。
上記のサンプルでは5〜100までが正常値として認識されます。
サポートされている型はBigDecimal、BigInteger、CharSequence、byte(Byte), short(Short), int(Integer), long(Long)です。

MaxアノテーションはDecimalMaxと同様、MinアノテーションはDecimalMinと同様です。
それぞれのvalue値はlong型を指定するようになっています。
サポートされている型はBigDecimal、BigInteger、byte(Byte), short(Short), int(Integer), long(Long)です。

Sizeアノテーションは長さの指定を行うことができます。
サンプルでは5〜10までの長さが正常値として認識されます。
サポートされている型はCharSequence、Collection、Map、Arrayです。

Digitsアノテーションは数値の桁数を指定します。
サンプルでは整数が3桁まで、少数が2桁までが正常値として認識されます。
サポートされている型はBigDecimal、BigInteger、CharSequence、byte(Byte), short(Short), int(Integer), long(Long)です。

Futureアノテーションは未来日が、Pastアノテーションは過去日が正常値として認識されます。
サポートされている型はDate、Calendarです。

【Validationを実行する】

サンプルのソースはJAX-RSのリソースクラスです。

[Validate方法]
package jp.glory.sample01.resources;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.ws.rs.BeanParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import jp.glory.sample01.bean.ValidationBaseBean;


@Path("/sample01")
public class ValidationBaseResource {

    @Inject
    private Validator validator = null;
    
    @POST
    public Response postRequest(@BeanParam final ValidationBaseBean bean) throws ParseException {

        // StringをDateに変換
        bean.setFutureDate(convertDate(bean.getFutureDateValue()));
        bean.setPastDate(convertDate(bean.getPastDateValue()));
        final Set<ConstraintViolation<ValidationBaseBean>> result =  validator.validate(bean);

        if (result.isEmpty()) {

            return createSuccessResponse();
        }

        return createErrorResponse(result);
    }

    private Date convertDate(final String dateValue) throws ParseException {
        final SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");

        if (dateValue.isEmpty()) {
            return null;
        }
        return format.parse(dateValue);
    }

    private Response createErrorResponse(final Set <ConstraintViolation<ValidationBaseBean>> validateResult) {
        
        // 後述
    }

    private Response createSuccessResponse() {
        
        // エラーがなかった場合のResponseを返却する
    }
}

Validationを行うにはValidator#validateメソッドにパラメータとしてBeanを渡します。
エラーの結果はjavax.validation.ConstraintViolationクラスのオブジェクトに格納され、
Setとして返却されます。

ConstraintViolationにはチェック対象のBeanのクラスを指定します。

[ConstraintViolation]

サンプルではConstraintViolationの中身を
HTMLとして出力します。

    private Response createErrorResponse(final Set <ConstraintViolation<ValidationBaseBean>> validateResult) {
        
        final StringBuilder builder = new StringBuilder();
        
        builder.append("<html>");
        builder.append("<head>");
        builder.append("<meta charset=\"utf-8\">");
        builder.append("<title>Validation</title>");
        builder.append("</head>");
        builder.append("<body>");

        for (final ConstraintViolation<ValidationBaseBean> result : validateResult) {
            
            builder.append("<div>");
            builder.append(result.getPropertyPath().toString());
            builder.append(" : ");
            builder.append(result.getInvalidValue());
            builder.append(" : ");
            builder.append(result.getMessage());
            builder.append("</div>");
        }

        builder.append("</body>");
        builder.append("</html>");
     
        return Response.ok(builder.toString()).build();
    }

getPropertyPathメソッドで対象のプロパティ名が、
getInvalidValueメソッドで設定された値が、
getMessageメソッドでメッセージが取得できます。

【まとめ】

アノテーションのみで入力チェックが行えました。

JavaEE6からは仕様として取り込まれているので、
簡単な入力チェックには利用できると思います。