Help us understand the problem. What is going on with this article?

JSFのValidatorにおいて2回目のgetSubmittedValue()で値がnullになった時の対応方法

More than 3 years have passed since last update.

環境
OS : macOS Hight Sierra Version 10.13.2
Eclipse : Neon.3 Release (4.6.3)
Java : JDK8
GlassFish : 4.1.2

事象 : JSFのValidatorにおいて2回目のgetSubmittedValue()で値がnullになる

スクリーンショット 2018-01-16 21.25.39.png

inputPassword.xhtml(画面)
<省略>
  <h:outputLabel>パスワードを入力して下さい。</h:outputLabel>
  <br />
  <h:inputSecret id="password" value="#{passwordBean.password}">
    <f:validateRequired />
    <f:validateLength minimum="3" maximum="10" />
    <f:validator validatorId="passwordValidator" />
    <f:attribute name="target" value="password" />
  </h:inputSecret>
  <h:message for="password" errorClass="error" />
  <br />
  <h:outputLabel>確認のためにもう&nbsp;&nbsp;1度入力して下さい。</h:outputLabel>
  <br />
  <h:inputSecret id="rePassword" value="#{passwordBean.kakuninPassword}">
    <f:validateRequired />
    <f:validator validatorId="passwordValidator" />
    <f:attribute name="target" value="rePassword" />
    <f:attribute name="passwordId" value="password" />
  </h:inputSecret>
  <h:message for="rePassword" errorClass="error" />
<省略>
PasswordValidator.java(カスタムバリデータ)
<省略>
@FacesValidator(value = "passwordValidator")
public class PasswordValidator implements Validator {
    /** パスワードに使っちゃダメな文字列. */
    private static final String KINSHI = "password";
    /** チェック対象を指定するname属性の名前. */
    private static final String TARGET_KEY = "target";
    /** パスワードをチェックする場合に指定するname属性の値. */
    private static final String TARGET_PASSWORD = "password";
    /** 確認パスワードをチェックする場合に指定するname属性の値. */
    private static final String TARGET_RE_PASSWORD = "rePassword";
    /** パスワード入力欄のid. */
    private static final String PASSWORD_ID_KEY = "passwordId";

    /*
     * (non-Javadoc)
     * @see javax.faces.validator.Validator#validate(javax.faces.context.
     * FacesContext, javax.faces.component.UIComponent, java.lang.Object)
     */
    @Override
    public void validate(FacesContext context, UIComponent component, Object value)
            throws ValidatorException {
        String target = (String) component.getAttributes().get(TARGET_KEY);
        if (TARGET_PASSWORD.equals(target)) {
            validatePassword(value);
        }
        if (TARGET_RE_PASSWORD.equals(target)) {
            validateRePassword(component, value);
        }
    }

    /**
     * 入力されたパスワードを検証する.
     * @param value 入力値.
     */
    private void validatePassword(Object value) {
        String inputedValue = (String) value;
        if (inputedValue.equals(KINSHI)) {
            FacesMessage errorMessage = new FacesMessage(KINSHI + "は使えません。");
            errorMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
            throw new ValidatorException(errorMessage);
        }
    }

    /**
     * 確認用に再入力されたパスワードを検証する.
     * @param component {@link UIComponent}
     * @param value 入力値.
     */
    private void validateRePassword(UIComponent component, Object value) {
        String rePassword = (String) value;
        String passwordId = (String) component.getAttributes().get(PASSWORD_ID_KEY);
        if (StringUtils.isNotBlank(passwordId)) {
            String password = getInputedPasswordValue(component, passwordId);
            if (StringUtils.isNotEmpty(rePassword)) {
                if (!rePassword.equals(password)) {
                    FacesMessage errorMessage = new FacesMessage("入力されたパスワードと一致しません。");
                    errorMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
                    throw new ValidatorException(errorMessage);
                }
            }
        }
    }

    /**
     * inputタグのid属性値からパスワードの入力値を取得する.
     * @param component {@link UIComponent}
     * @param id inputタグのid属性値.
     * @return パスワードの入力値.
     */
    private String getPasswordValueById(UIComponent component, String id) {
        HtmlInputSecret inputTextConf = (HtmlInputSecret) component.findComponent(id);
        Object submittedValue = inputTextConf.getSubmittedValue();
        return submittedValue.toString();
    }
}

2回目に値がnullにならないパターン : 1回目の検証でエラー

スクリーンショット 2018-01-16 21.42.57.png

スクリーンショット 2018-01-16 21.43.24.png

スクリーンショット 2018-01-16 21.43.54.png

スクリーンショット 2018-01-16 21.44.25.png

2回目に値がnullになるパターン : 1回目の検証で正常

スクリーンショット 2018-01-16 21.45.27.png

スクリーンショット 2018-01-16 21.45.51.png

スクリーンショット 2018-01-16 21.46.46.png

理由 : 検証が終わってsetSubmittedValue(null)が実行されているから

image.png
JSFのライフサイクル
1. Viewの復元
2. 入力値の適用
3. 入力値の変換と検証
4. バッキングビーンの変数に値をバインド
5. 処理の実行
6. レスポンスのレンダリング

今回は、このライフサイクルの3で1回目の検証においてエラーになるとgetSubmittedValue()で値がnullになりました。

JSF 2.0 の詳細について | 寺田 佳央 - Yoshio Terada
2回目以降のリクエスト(必要項目に記入してボタンを押下したような場合)では、UIView のツリーが復元(ライフサイクルの1番目)された後、リクエスト値の適用フェーズ(ライフサイクルの2番目)で UIComponent に対して入力値を適用します。(この例では HtmlInput#setSubmittedValue()が実行される。) この、setSubmittedValue() された値は、入力値の検証フェーズ(ライフサイクルの3番目) が完了するまで、getSubmittedValue() で値を取り出す事ができます。入力値の検証フェーズが終わると、入力値は、setSubmittedValue(null)が実行され、HtmlInput#setValue(“foo”) が呼び出され、以降のフェーズでは入力値は HtmlInput#getValue() を利用して取得するようになります。

対応 : 2回目のgetSubmittedValue()で値がnullになったらgetValue()で値を取得する

<他の箇所は修正なし>
    private String getPasswordValueById(UIComponent component, String id) {
        HtmlInputSecret inputTextConf = (HtmlInputSecret) component.findComponent(id);
        Object submittedValue = inputTextConf.getSubmittedValue();
        if (submittedValue == null) {
            // getSubmittedValue()で値がnullになったらgetValue()で値を取得する
            submittedValue = inputTextConf.getValue();
        }
        return submittedValue.toString();
    }
<他の箇所は修正なし>

スクリーンショット 2018-01-16 22.48.50.png

スクリーンショット 2018-01-16 22.52.31.png

スクリーンショット 2018-01-16 22.52.43.png

スクリーンショット 2018-01-16 22.53.01.png

参考サイト:アノテーションでValidatorを仕掛ける時はこちらがさんこうになります

ponsuke0531
びっくりするほど物覚えが悪いが、エンジニアを目指しています。
http://ponsuke-tarou.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away