JSFメモ2

今回はJSFのValidatorについて。
JSFには3種類のデフォルトValidatorが存在します。

クラス::タグ
DoubleRangeValidator ::validateDoubleRange
LengthValidator ::validateLength
LongRangeValidator ::validateLongRange

文字列の長さをチェックしたい場合などは以下のように記述します。
この場合、価格を入力させて送信ボタンを押すと、入力された価格の長さが「8文字」以上の場合に<h:message>タグの位置にエラーメッセージが表示されます。

<h:form>
    <h:inputText id="price" value="#{Sample.price}"
        <f:validateLength maximum="8"/>
    </h:inputText>
    <h:message for="price" />
    <h:commandButton value="送信" action="action01"/>
</h:form>

しかし、デフォルトでチェック出来る事には限りがある為、自前のValidatorクラスを作る事も可能です。流れとしては、

  1. 自前のValidatorクラスを作成する
  2. faces-config.xmlに作成したvalidatorを追加する
  3. jspで登録したvalidatorを呼び出す

こんな感じ。

それでは、順に追っていきましょう。

1. 自前のValidatorクラスを作成する
Validatorクラスを作るにはValidatorインタフェースを実装すればよさげ。

package org.myapp.validator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class NumberValidator implements Validator {

  /**
   * 数値チェックを行います。
   * @param context
   * @param component
   * @param value
   * @throws ValidatorException
   */
  public void validate(FacesContext context, UIComponent component, Object value) 
   throws ValidatorException {
    String text = value.toString();
    try {
      int num = Integer.parseInt(text);
      if (num == 0) {
        showErrorMessage("0を設定する事はできません。");
      }
    } catch (NumberFormatException ne) {
      showErrorMessage("数値以外を入力する事はできません。");
    }
  }
  
  /**
   * 引数で与えられた文字列をMessageコンポーネントに表示する。
   * @param value - エラーメッセージ
   */
  private void showErrorMessage(String errorMessage){
      FacesMessage message = new FacesMessage(errorMessage);
      message.setSeverity(FacesMessage.SEVERITY_FATAL);
      throw new ValidatorException(message);
  }
}

2. faces-config.xmlに作成したvalidatorを追加する
次に作成したクラスを登録します。

  <validator>
    <validator-id>OrgNumberValidator</validator-id>
    <validator-class>org.myapp.validator.NumberValidator</validator-class>
  </validator>

これは簡単。

3. jspで登録したvalidatorを呼び出す
後は呼び出すだけ。

<h:form>
    <h:inputText id="price" value="#{Sample.price}"
        <f:validator validatorId="OrgNumberValidator"  />
    </h:inputText>
    <h:message for="price" />
    <h:commandButton value="送信" action="action01"/>
</h:form>

さあ、できた。というわけで価格を「0」にして送信!
ちゃんと、「0を設定する事はできません。」というメッセージが表示されました。
次に価格に「一万円」と入力して送信!

"price": 正しい値を入力して下さい.

あれ?意図したメッセージと違う...。デバッグ用のログとかを組み込んでみても、「一万円」と入力した場合は、Validatorクラスも呼ばれていないっぽい。
いろいろ調べてみると、JSFのライフサイクルでは「Validator」の前に「Converter」なるものが走るようです。この「Converter」はフォームに入力されたデータをマーネージドビーン(上記サンプルではSampleという名前)にマッピングする際、型変換を行うというもの。確かにSampleクラスではpriceをintで持っているので、「一万円」(文字列)はintに変換できないな。
で、このエラーメッセージはどこに記述してあるかというと、myfaces.jar(私の場合はmyfaces-impl-1.1.3.jarでした)のjavax/faces/Messages.propertiesの中。で、こいつを上書きする為には自前のリソースバンドルに同じプロパティ名で上書きしてやれば良いようです。

javax/faces/Messages.properties

javax.faces.convert.IntegerConverter.CONVERSION        = 変換エラー
javax.faces.convert.IntegerConverter.CONVERSION_detail = "{0}": 正しい値を入力して下さい.

とあったので、リソースバンドルに以下のように記述。
(※自分の場合は/WEB-INF/classes/org/myapp/resources/ApplicationResources.properties)

javax.faces.convert.IntegerConverter.CONVERSION        = 変換エラー
javax.faces.convert.IntegerConverter.CONVERSION_detail = "{0}": 正しいInteger値を入力して下さい.

これで再度「一万円」と入力し実行!

"price": 正しいInteger値を入力して下さい.

OK。ちゃんと上書きされました。なるほど、型チェックはValidatorを使わずとも勝手にやってくれるのだなー。