JakartaEE(JavaEE)

JSFプロジェクトでチェック処理を実装してみた

JSF(JavaServer Faces)プロジェクトを利用して、JavaベースのWebアプリケーションを作成することができるが、その際に、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。

今回は、JSFプロジェクトでチェック処理を実装してみたので、そのサンプルプログラムを共有する。

前提条件

以下の記事の実装が完了していること。

JSFプロジェクトで画面遷移をfaces-config.xmlに集約してみたJSF(JavaServer Faces)プロジェクトでWebアプリケーションを作成する際、これまでは画面遷移を@Namedアノテーショ...

また、JSFプロジェクト作成時のJSFのバージョンは、2.2であること。
JSF_バージョン



削除または保存していないWordドキュメントの復元方法【4DDiG Windowsデータ復元】ワード(Word)データ等のファイルを誤って削除してしまった場合は、通常はデータの復元ができませんが、4DDiGというソフトウェアを利用...

サンプルプログラムの作成

作成したサンプルプログラムの構成は、以下の通り。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。

入力画面(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();
    }

}
「CODE×CODE」は、需要の高い技術(AWS, Python等)を習得できるプログラミングスクールスクールだった近年、さまざまな会社でクラウド(特にIaaSやPaaSのパブリッククラウド)の需要が非常に高まっていて、クラウドサービスによるシステム開...

さらに、以下のメッセージプロパティで、エラーメッセージを定義している。

#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」と同じパスにしている。
ValidationMessages

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

さらに、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



サラリーマン型フリーランスSEという働き方でお金の不安を解消しよう先日、「サラリーマン型フリーランスSE」という働き方を紹介するYouTube動画を視聴しましたので、その内容をご紹介します。 「サ...

サンプルプログラムの実行結果

サンプルプログラムの実行結果は以下の通りで、チェック処理が正常に行えることが確認できる。

1) GlassFishサーバーを起動後、Webブラウザ上で「http:// (ホスト名):(ポート番号)/(Webアプリケーションのプロジェクト名)/」とアクセスする。
サンプルプログラムの実行結果_1

2) 何も入力せずに「確認」ボタンを押下すると、以下のように、エラーメッセージが表示される。
サンプルプログラムの実行結果_2_1

サンプルプログラムの実行結果_2_2

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

サンプルプログラムの実行結果_3_2

4) 3)のエラーを解消すると、以下のように、確認画面に遷移することが確認できる。
サンプルプログラムの実行結果_4_1

サンプルプログラムの実行結果_4_2

要点まとめ

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