Bean Validation 2.0 を Java SE のプログラムに導入する

## Bean Validation とは

- Bean Validation はアノテーションを利用して入力値チェックをするAPI仕様
- Java SE 上で実行できる
- Java EE の 6 以降に入っている
- Java EE 8 には Bean Validation 2.0 が入っている

## Gradle で Java SE のプログラムに導入する例

build.gradle の dependencies に以下の3つを設定する。

- javax.validation:validation-api (Bean Validation API)
- org.hibernate.validator:hibernate-validator (Bean Validation API を実装したライブラリ)
- org.glassfish:javax.el (Unified Expression Language を実装したライブラリ)

例えば以下の build.gradle のように記述する。
dependencies で3つの依存ライブラリを指定している。

```build.gradle
plugins {
  id 'java'
  id 'application'
}

group 'com.example'
version '1.0'

sourceCompatibility = 11

repositories {
  mavenCentral()
}

dependencies {

  // https://mvnrepository.com/artifact/javax.validation/validation-api
  implementation 'javax.validation:validation-api:2.0.1.Final'

  // https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator
  runtimeOnly 'org.hibernate.validator:hibernate-validator:6.0.17.Final'

  // https://mvnrepository.com/artifact/org.glassfish/javax.el
  runtimeOnly 'org.glassfish:javax.el:3.0.1-b11'
}

application {
  mainClassName = 'com.example.App'
}
```

## 値の検証をするサンプルプログラム

Bean Validation 2.0 を使って値の検証をするサンプルプログラム。

```java
package com.example;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Set;

public class App {

  public static void main(String[] args) {

    // バリデーターを用意
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();

    // 入力データを用意
    String[] list = {null, "", "1234", "12345"};

    for (var s : list) {

      // 入力データを構築
      var input = new Input();
      input.data = s;

      // バリデーション実行
      Set<ConstraintViolation<Input>> v = validator.validate(input);

      if (v.isEmpty()) {
        System.out.println(input.data + ": " + "問題ないデータです");
      } else {
        // バリデーションに引っかかったとき
        for (var vv : v) {
          System.out.println(input.data + ": " + vv.getMessage());
        }
      }
    }
  }

  private static class Input {
    @NotNull(message = "nullはダメです")
    @NotEmpty(message = "空文字列はダメです")
    @Size(max = 4, message = "4文字を超えてはダメです")
    String data;
  }
}
```

実行結果。

```
null: 空文字列はダメです
null: nullはダメです
: 空文字列はダメです
1234: 問題ないデータです
12345: 4文字を超えてはダメです
```

## NoProviderFoundException が発生する場合

以下のようなエラーメッセージが出力される場合は、Bean Validation API を実装したライブラリがクラスパス上に見つかっていない。

> javax.validation.NoProviderFoundException: Unable to create a Configuration, because no Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.

Bean Validation API を実装した Hibernate Validator を使用するよう、build.gradle の dependencies に以下のように記述することで対処できる。

```
runtimeOnly 'org.hibernate.validator:hibernate-validator:6.0.17.Final'
```

## NoClassDefFoundError: javax/el/ELManager が発生する場合

以下のようなエラーメッセージが出力される場合は、Unified Expression Language を実装したライブラリがクラスパス上に見つかっていない。

> javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

Hibernate Validatorでは、制約違反メッセージの動的な式を評価するために、Unified Expression Language（JSR 341）の実装も必要となっている。
Java EE コンテナなどには含まれているが、Java SE では明示的に導入が必要。
org.glassfish の javax.el が JSR 341 のリファレンス実装とのことなのでこれを導入すればいい。
build.gradle の dependencies に以下のように記述することで対処できる。

```
runtimeOnly 'org.glassfish:javax.el:3.0.1-b11'
```

## 参考資料

- [Bean Validation \- Bean Validation 2\.0 \(JSR 380\)](https://beanvalidation.org/2.0/)
- [Getting started with Hibernate Validator \- Hibernate Validator](http://hibernate.org/validator/documentation/getting-started/)