環境
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になる
<省略>
<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>確認のためにもう 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" />
<省略>
<省略>
@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回目の検証でエラー
2回目に値がnullになるパターン : 1回目の検証で正常
理由 : 検証が終わってsetSubmittedValue(null)が実行されているから
- Viewの復元
- 入力値の適用
- 入力値の変換と検証
- バッキングビーンの変数に値をバインド
- 処理の実行
- レスポンスのレンダリング
今回は、このライフサイクルの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();
}
<他の箇所は修正なし>