shoota works

Hibernate ValidatorはTomcat6だと動かない

Hibernate ValidatorはTomcat6だと動かない
Photo by

Java な Web アプリで入力チェックを実装する上でとても便利なHibernate Validator
その名の通り、一連の Validation を annotation ベースで実装できる。元の仕様はJSR 303と呼ばれるもので、Spring MVC では 3.x 系から取り込まれたらしく、annotation-driven なコードが書ける。

だけど、Tomcat6 だとチョットだけ問題があった。

Hibernate Validator?

JSR 303で定義されている annotation ベースの validation に独自の拡張
を与えたもの。Web アプリでの入力チェックあるあるをほぼ網羅しているので、よく使われる形式のデータは annotation を記述するだけで入力チェックとして機能する。

Maven プロジェクトなら、pom.xml に追加すればよい(現時点での最新は 5.x 系)。って、ここに書いてる。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.2.Final</version>
</dependency>

実装例

/**
 * Human.java
 */
public class Human {
 
  // 必須入力!
  @NotNull
  private String name;
 
  public String getName(){
    return this.name;
  }
 
  public void setName(String name){
    this.name = name;
  }
 
}
/**
 * RootController.java
 */
@RequestMapping("/")
@Controller
public class RootController {
 
    /**
     * app rootへのアクセス
     */
    @RequestMapping(method = RequestMethod.GET)
    public String index(
        @Valid @ModelAttribute Human human,
        BindingResult bindingResult
        ) {
 
        if(bindingResult.hasErrors()){
          // 入力チェックエラーの処理
        }
 
        return "index";
    }
}
 

@Valid Humanannotation で@NotNullのチェックを実行させ、その結果をBindeingResultが保持(参照?)している。

複数の制約で一つの入力チェックとしたい場合は独自で annotation を実装する。これについてはこのエントリ
非常にわかりやすくてためになった。ちなみに、validation のエラー時のメッセージは propety ファイルで管理されているので、メッセージの日本語化に関しても、上記のエントリがすごく役立った。ありがとうござます。

本題

で、だ。悲しい事件が起きたのはサーバにデプロイしたときである。なんか見覚えのないエラーが吐かれた。

NoSuchMethodError: javax.el.ExpressionFactory.newInstance()Ljavax/el/ExpressionFactory)

ググる。あった。

Hibernate Validator 5 requires the Unified Expression Language (EL) in version 2.2 or later.
While Tomcat 7 provides EL 2.2 out of the box, Tomcat 6 only comes with an EL 2.1 implementation which does not work with Hibernate Validator.

$CATALINE_HOME/libの EL が 2.2 と 2.1 の間で互換性がないせいで、5.x 系は tomcat6 そのままだと動かないんだー。

You can therefore either upgrade to Tomcat 7 or update the EL libs in your Tomcat 6 installation.
To do the latter, replace the JAR files el-api.jar and and jasper-el.jar in $CATALINE_HOME/lib with an EL 2.2 implementation, e.g. from Tomcat 7.

Tomcat7 の lib から 2.2 を持ってきて上書きすればいいよ、と添えられています。

僕の環境では jar に対してシンボリックリンクが貼られていたので、上書きではなくリンク先をすげ替えることで無事動作しました。

おまけ

そもそも EL(el-api.jar)ってなに?

JSR 341で 3.0 について説明されていました。

上の例だと、Human.classのインスタンスhumanのプロパティに対して、通常の java コードではアクセサメソッドを経由しなければならない。
一方 JSTL を呼び込んでいる JSP などではhuman.nameで取得できる。この辺を旨いことやってくれているものが EL(Unified Expression Language)らしい。
CDI にも関わるとか。詳細はわかりません。