JSF(JavaServer Faces)プロジェクトを利用して、JavaベースのWebアプリケーションを作成することができるが、その際に、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。
今回は、JSFプロジェクトでチェック処理を実装してみたので、そのサンプルプログラムを共有する。
前提条件
以下の記事の実装が完了していること。
また、JSFプロジェクト作成時のJSFのバージョンは、2.2であること。
サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
入力画面(input.xhtml)の内容は以下の通りで、各項目でチェックエラーになった場合のエラーメッセージを表示できるようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | <?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メソッドで実施している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | 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(); } } |
さらに、以下のメッセージプロパティで、エラーメッセージを定義している。
1 2 3 4 5 6 7 8 9 10 | #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」を読めるようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?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ファイルの内容は、以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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; } } |
1 2 3 4 5 6 7 8 9 | .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)プロジェクトでは、アノテーションの利用や独自メソッドの実装によって、チェック処理を行うことができる。