JSF(JavaServer Faces)プロジェクトを利用して、JavaベースのWebアプリケーションを作成することができるが、その際に、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。
今回は、JSFプロジェクトでチェック処理を実装してみたので、そのサンプルプログラムを共有する。
前提条件
以下の記事の実装が完了していること。
また、JSFプロジェクト作成時のJSFのバージョンは、2.2であること。

サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。

なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
入力画面(input.xhtml)の内容は以下の通りで、各項目でチェックエラーになった場合のエラーメッセージを表示できるようにしている。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>入力画面</title>
<h:outputStylesheet library="css" name="demoJsf.css"/>
</h:head>
<h:body>
<p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/>
<h:form>
<table border="0">
<tr>
<td align="left" valign="top">名前:</td>
<td align="left" valign="top">
<h:inputText id="name" value="#{inputFormAction.name}" />
</td>
<td align="left" valign="top">
<h:messages for="name" errorClass="errorMessage" layout="table" />
</td>
</tr>
<tr>
<td align="left" valign="top">生年月日:</td>
<td align="left" valign="top">
<!-- 生年月日_年・月・日のチェックを行うため、
InputFormActionクラスのvalidateメソッドを呼び出す -->
<h:outputLabel id="birth">
<f:event listener="#{inputFormAction.validate}" type="postValidate" />
<h:inputText id="birthYear" value="#{inputFormAction.birthYear}" size="4" />
年
<h:selectOneMenu id="birthMonth" value="#{inputFormAction.birthMonth}">
<f:selectItems value="#{inputFormAction.birthMonthItems}"/>
</h:selectOneMenu>月
<h:selectOneMenu id="birthDay" value="#{inputFormAction.birthDay}">
<f:selectItems value="#{inputFormAction.birthDayItems}"/>
</h:selectOneMenu>日
</h:outputLabel>
</td>
<td align="left" valign="top">
<h:messages for="birth" errorClass="errorMessage" layout="table" />
</td>
</tr>
<tr>
<td align="left" valign="top">性別:</td>
<td align="left" valign="top">
<h:selectOneRadio id="sex" value="#{inputFormAction.sex}">
<f:selectItems value="#{inputFormAction.sexItems}" />
</h:selectOneRadio> </td>
<td align="left" valign="top">
<h:messages for="sex" errorClass="errorMessage" layout="table" />
</td>
</tr>
<tr>
<td align="left" valign="top">メモ:</td>
<td align="left" valign="top">
<h:inputTextarea value="#{inputFormAction.memo}" cols="40" rows="6" />
</td>
<td align="left" valign="top"></td>
</tr>
<tr>
<td align="left" valign="top">入力確認:</td>
<td align="left" valign="top">
<h:selectBooleanCheckbox id="checked" value="#{inputFormAction.checked}" />
</td>
<td align="left" valign="top">
<h:messages for="checked" errorClass="errorMessage" layout="table" />
</td>
</tr>
</table>
<br/>
<h:commandButton value="確認" action="#{inputFormAction.confirm()}" />
</h:form>
</h:body>
</html>また、各画面のForm値と画面遷移を定義したクラスの内容は以下の通りで、名前・性別・確認チェックの単項目チェックをアノテーションで追加し、生年月日の相関チェックをvalidateメソッドで実施している。
package faces;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.model.SelectItem;
import javax.inject.Named;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import common.CommonUtil;
import lombok.Data;
import lombok.ToString;
/**
* 画面のフォーム値と画面遷移メソッドを定義.
*/
// @Namedアノテーションは、JSFのXHTMLファイルから#{inputFormAction}で
// Javaクラスを参照できるようにしている(→バッキングビーン)
// @SessionScopedアノテーションは、このバッキングビーンの生存期間を
// セッションに設定している
@Named(value="inputFormAction")
@SessionScoped
// 以下はLombokのアノテーション
//「@Data」アノテーションを付与すると、このクラス内の全フィールドに対する
// Getterメソッド・Setterメソッドにアクセスができる
@Data
//「@ToString」アノテーションを用いて、exclude属性で指定した項目以外の値を、
// toStringメソッド呼出時に出力することができる
@ToString(exclude={"birthMonthItems","birthDayItems","sexItems"})
public class InputFormAction implements Serializable {
// シリアルバージョンUID
private static final long serialVersionUID = 7283339629129432007L;
/** 名前 */
@NotEmpty
@Size(min=1, max=10)
private String name;
/** 生年月日_年 */
private String birthYear;
/** 生年月日_月 */
private String birthMonth;
/** 生年月日_日 */
private String birthDay;
/** 性別 */
@NotEmpty(message="{sex.NotEmpty.message}")
private String sex;
/** 性別(ラベル) */
private String sexLabel;
/** メモ */
private String memo;
/** 確認チェック */
@AssertTrue
private Boolean checked;
/** 生年月日_月(選択リスト) */
private List<SelectItem> birthMonthItems;
/** 生年月日_日(選択リスト) */
private List<SelectItem> birthDayItems;
/** 性別(選択リスト) */
private List<SelectItem> sexItems;
/**
* コンストラクタ生成時に選択リストの値を設定.
*/
public InputFormAction(){
// 生年月日_月(選択リスト)
birthMonthItems = new ArrayList<SelectItem>();
birthMonthItems.add(new SelectItem("", ""));
for(Integer i = 1; i <= 12; i++){
birthMonthItems.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
}
// 生年月日_日(選択リスト)
birthDayItems = new ArrayList<SelectItem>();
birthDayItems.add(new SelectItem("", ""));
for(Integer i = 1; i <= 31; i++){
birthDayItems.add(new SelectItem(String.valueOf(i), String.valueOf(i)));
}
// 性別(選択リスト)
sexItems = new ArrayList<SelectItem>();
sexItems.add(new SelectItem(String.valueOf(1),"男"));
sexItems.add(new SelectItem(String.valueOf(2),"女"));
}
/**
* 確認画面への遷移.
* @return 確認画面へのパス
*/
public String confirm(){
// 性別(ラベル)を設定
if(!CommonUtil.isBlank(sex)){
this.setSexLabel(this.getSexItems().get(
Integer.parseInt(this.getSex())-1).getLabel());
}
// Formに設定された値を出力
System.out.println(this.toString());
// 確認画面に遷移
return "confirm";
}
/**
* 入力画面に戻る.
* @return 入力画面へのパス
*/
public String back(){
// 入力画面に戻る
return "back";
}
/**
* 完了画面への遷移.
* @return 完了画面へのパス
*/
public String send(){
// 確認画面に表示された値を出力
System.out.println(this.toString());
// 完了画面への遷移
return "send";
}
/**
* 相関チェックを実施し、エラーの場合はエラーメッセージを表示.
* @param compSysEvent JSFシステムイベント
*/
public void validate(ComponentSystemEvent compSysEvent) {
UIComponent component = compSysEvent.getComponent();
// 生年月日の年・月・日を取得する
UIInput birthYearUI = (UIInput)component.findComponent("birthYear");
UIInput birthMonthUI = (UIInput)component.findComponent("birthMonth");
UIInput birthDayUI = (UIInput)component.findComponent("birthDay");
String birthYearSt = (String)birthYearUI.getLocalValue();
String birthMonthSt = (String)birthMonthUI.getLocalValue();
String birthDaySt = (String)birthDayUI.getLocalValue();
// 年・月・日がすべて空白値の場合はエラーメッセージを返す
if(CommonUtil.isBlank(birthYearSt) && CommonUtil.isBlank(birthMonthSt)
&& CommonUtil.isBlank(birthDaySt)){
addErrorMessage("org.hibernate.validator.constraints.NotEmpty.message"
, component);
return;
}
// 生年月日が存在しない日付の場合はエラーメッセージを返す
String dateStr = birthYearSt + CommonUtil.addZero(birthMonthSt)
+ CommonUtil.addZero(birthDaySt);
if(!CommonUtil.isCorrectDate(dateStr, "uuuuMMdd")){
addErrorMessage("date.Invalid.message", component);
}
}
/**
* 引数のメッセージKeyをもつエラーメッセージを追加.
* @param messageKey メッセージKey
* @param component JSFコンポーネント
*/
private void addErrorMessage(String messageKey, UIComponent component){
FacesContext context = FacesContext.getCurrentInstance();
String message = CommonUtil.getMessage(messageKey);
FacesMessage facesMessage = new FacesMessage(message, message);
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(component.getClientId(), facesMessage);
context.renderResponse();
}
} さらに、以下のメッセージプロパティで、エラーメッセージを定義している。
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
org.hibernate.validator.constraints.NotEmpty.message=値を入力してください。
javax.validation.constraints.Size.message={min}文字以上{max}文字以下で入力してください。
date.Invalid.message=不正な日付になっています。
sex.NotEmpty.message=性別を指定してください。
javax.validation.constraints.AssertTrue.message=チェックを指定してください。なお、上記プロパティファイルのパスは、以下のbean-validator.jarに含まれる「ValidationMessages.properties」と同じパスにしている。

また、プロパティファイルの編集には、以下の「Limy プロパティー・エディター」を利用している。

さらに、faces-config.xmlの内容は以下の通りで、日本語をサポートし、日本語のメッセージ「ValidationMessages_ja.properties」を読めるようにしている。
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<!-- 入力画面から確認画面への遷移 -->
<!-- ただし、リダイレクトさせるために、redirectタグを追加 -->
<navigation-rule>
<from-view-id>/input.xhtml</from-view-id>
<navigation-case>
<from-outcome>confirm</from-outcome>
<to-view-id>/confirm.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<!-- 確認画面から完了画面・入力画面への遷移 -->
<!-- ただし、リダイレクトさせるために、redirectタグを追加 -->
<navigation-rule>
<from-view-id>/confirm.xhtml</from-view-id>
<navigation-case>
<from-outcome>send</from-outcome>
<to-view-id>/complete.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>back</from-outcome>
<to-view-id>/input.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<!-- サポートしている言語を日本語に指定 -->
<application>
<locale-config>
<default-locale>ja_JP</default-locale>
<supported-locale>ja</supported-locale>
<supported-locale>ja_JP</supported-locale>
</locale-config>
</application>
</faces-config>その他、共通ユーティリティクラス、入力画面(input.xhtml)で読んでいるCSSファイルの内容は、以下の通り。
package common;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* 共通ユーティリティクラス.
*/
public class CommonUtil {
/**
* 引数で指定した文字列がNULL,空文字,空白のみかどうかをチェックする.
* @param cs 文字列
* @return 判定結果
*/
public static boolean isBlank(final CharSequence cs) {
final int strLen = length(cs);
if (strLen == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
/**
* 引数で指定した文字列の長さを返却する.
* @param cs 文字列
* @return 文字列の長さ
*/
public static int length(final CharSequence cs) {
return cs == null ? 0 : cs.length();
}
/**
* DateTimeFormatterを利用して日付チェックを行う.
* @param dateStr チェック対象文字列
* @param dateFormat 日付フォーマット
* @return 日付チェック結果
*/
public static boolean isCorrectDate(String dateStr, String dateFormat){
if(CommonUtil.isBlank(dateStr) || CommonUtil.isBlank(dateFormat)){
return false;
}
//日付と時刻を厳密に解決するスタイルで、DateTimeFormatterオブジェクトを作成
DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat)
.withResolverStyle(ResolverStyle.STRICT);
try{
//チェック対象文字列をLocalDate型の日付に変換できれば、チェックOKとする
LocalDate.parse(dateStr, df);
return true;
}catch(Exception e){
return false;
}
}
/**
* 数値文字列が1桁の場合、頭に0を付けて返す.
* @param intNum 数値文字列
* @return 変換後数値文字列
*/
public static String addZero(String intNum){
if(CommonUtil.isBlank(intNum)){
return intNum;
}
if(intNum.length() == 1){
return "0" + intNum;
}
return intNum;
}
/**
* 引数のメッセージKeyに該当するメッセージを取得する.
* @param messageKey メッセージKey
* @return メッセージ
*/
public static String getMessage(String messageKey){
ResourceBundle bundle = null;
String value = null;
try{
bundle = ResourceBundle.getBundle(
"org.hibernate.validator.ValidationMessages_ja");
value = bundle.getString(messageKey);
}catch(MissingResourceException ex){
System.err.println(ex);
return null;
}
return value;
}
}.lineBreakFormat{
/* 改行コードをbrタグに変換し表示する */
/* 空白文字はそのまま残す */
white-space: pre;
}
.errorMessage{
/* エラーメッセージを赤字で表示する */
color: #FF0000;
}その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/javaee-jsf-web-check/demoJsf
サンプルプログラムの実行結果
サンプルプログラムの実行結果は以下の通りで、チェック処理が正常に行えることが確認できる。
1) GlassFishサーバーを起動後、Webブラウザ上で「http:// (ホスト名):(ポート番号)/(Webアプリケーションのプロジェクト名)/」とアクセスする。

2) 何も入力せずに「確認」ボタンを押下すると、以下のように、エラーメッセージが表示される。


3) 名前と生年月日で必須入力チェック以外のエラーが出るよう入力し、「確認」ボタンを押下すると、以下のエラーメッセージが表示される。


4) 3)のエラーを解消すると、以下のように、確認画面に遷移することが確認できる。


要点まとめ
- JSF(JavaServer Faces)プロジェクトでは、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。





