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)プロジェクトでは、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。