JBoss Seamとは、Java EEベースで、JSF(JavaServer Faces)からEJB(Enterprise Java Bean) 3.0、JPA(Enterprise Java Bean)まで一貫したコンポーネントモデルで「つなぐ」Webアプリケーション開発用フレームワークで、いくつかのアノテーションを使うだけでEJBとJSFをつなぎ合わせることができるようになっている。
今回は、JBoss Seamアプリケーションで入力画面・確認画面・完了画面の3画面を含み、HTMLオブジェクトとしてテキストボックス・ラジオボタン・チェックボックス等を含むWEBアプリケーションを作成してみたので、そのサンプルプログラムを共有する。
前提条件
下記記事に記載した、JBoss Seamアプリケーションで初期表示画面(input.xhtml)の追加が完了していること。
さらに、以下の手順に従って、Lombokの導入が完了していること。
サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
入力画面(input.xhtml)、確認画面(confirm.xhtml)、完了画面(complete.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 | <?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,h:bodyタグを使うとエラーになるため、head,bodyタグにそれぞれ変更 --> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>入力画面</title> </head> <body> <p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/> <h:form> <table border="0"> <tr> <td align="left" valign="top">名前:</td> <td><h:inputText value="#{inputFormAction.name}" /></td> </tr> <tr> <td align="left" valign="top">生年月日:</td> <td> <h:inputText value="#{inputFormAction.birthYear}" size="4" maxlength="4" />年 <h:selectOneMenu value="#{inputFormAction.birthMonth}"> <f:selectItems value="#{inputFormAction.birthMonthItems}"/> </h:selectOneMenu>月 <h:selectOneMenu value="#{inputFormAction.birthDay}"> <f:selectItems value="#{inputFormAction.birthDayItems}"/> </h:selectOneMenu>日 </td> </tr> <tr> <td align="left" valign="top">性別:</td> <td> <h:selectOneRadio value="#{inputFormAction.sex}"> <f:selectItems value="#{inputFormAction.sexItems}"/> </h:selectOneRadio> </td> </tr> <tr> <td align="left" valign="top">メモ:</td> <td><h:inputTextarea value="#{inputFormAction.memo}" cols="40" rows="6" /></td> </tr> <tr> <td align="left" valign="top">入力確認:</td> <td><h:selectBooleanCheckbox value="#{inputFormAction.checked}" /></td> </tr> </table> <br/> <h:commandButton value="確認" action="#{inputFormAction.confirm()}" /> </h:form> </body> </html> |
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 | <?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:a4j="http://richfaces.org/a4j"> <!-- h:head,h:bodyタグを使うとエラーになるため、head,bodyタグにそれぞれ変更 --> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>確認画面</title> <!-- cssファイルの読み込みをseams用に変更 --> <a4j:loadStyle src="resource:///stylesheet/demoJsf.css" /> </head> <body> <p>入力内容を確認し、問題なければ「送信」ボタンを押下してください。</p><br/> <h:form> <table border="0"> <tr> <td align="left" valign="top">名前:</td> <td><h:outputText value="#{inputFormAction.name}" /></td> </tr> <tr> <td align="left" valign="top">生年月日:</td> <td><h:outputText value="#{inputFormAction.birthYear}年 #{inputFormAction.birthMonth}月 #{inputFormAction.birthDay}日" /></td> </tr> <tr> <td align="left" valign="top">性別:</td> <td><h:outputText value="#{inputFormAction.sexLabel}" /></td> </tr> <tr> <td align="left" valign="top">メモ:</td> <td><h:outputText value="#{inputFormAction.memo}" styleClass="lineBreakFormat" /></td> </tr> <tr> <td align="left" valign="top">確認チェック:</td> <td> <h:outputText value="確認済" rendered="#{inputFormAction.checked}" /> <h:outputText value="未確認" rendered="#{inputFormAction.checked == false}" /> </td> </tr> </table> <br/> <h:commandButton value="送信" action="#{inputFormAction.send()}" /> <h:outputText value=" "/> <!-- で半角空白が設定されないため、上記のように変更 --> <h:commandButton value="戻る" action="#{inputFormAction.back()}" /> </h:form> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?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"> <!-- h:head,h:bodyタグを使うとエラーになるため、head,bodyタグにそれぞれ変更 --> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>完了画面</title> </head> <body> <p>お申し込みが完了しました。</p> </body> </html> |
上記XHTMLファイルは、以下の記事のinput.xhtml、confirm.xhtml、complete.xhtmlのソースコードをそれぞれ流用していて、それぞれ、h:head,h:bodyタグをhead,bodyタグに変更している。また、確認画面のCSSファイル読込方法を変更している。
また、各画面のForm値と画面遷移処理を定義したクラスの内容は、以下の通り。
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 | package faces; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.faces.model.SelectItem; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import common.CommonUtil; import lombok.Data; import lombok.ToString; /** * 画面のフォーム値と画面遷移メソッドを定義. */ // 会話スコープを指定 // 会話スコープの開始・終了タイミングはpages.xmlで指定 @Scope(ScopeType.CONVERSATION) // @Nameアノテーションは、JSFのXHTMLファイルから#{inputFormAction}で // Javaクラスを参照できるようにしている(→バッキングビーン) // ただし、org.jboss.seam.annotations.Nameクラスなので注意 @Name("inputFormAction") @Data @ToString(exclude={"birthMonthItems","birthDayItems","sexItems"}) public class InputFormAction implements Serializable { // シリアルバージョンUID private static final long serialVersionUID = 7283339629129432007L; /** 名前 */ private String name; /** 生年月日_年 */ private String birthYear; /** 生年月日_月 */ private String birthMonth; /** 生年月日_日 */ private String birthDay; /** 性別 */ private String sex; /** 性別(ラベル) */ private String sexLabel; /** メモ */ private String memo; /** 確認チェック */ 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"; } } |
上記Actionファイルは、以下の記事のInputFormAction.javaをそれぞれ流用していて、クラスの先頭に、seamの@Scopeアノテーションと@Nameアノテーションを付与するよう、変更している。また、確認チェックをString型からBoolean型に変更している。
さらに、画面遷移を定義したpages.xmlの定義は以下の通りで、会話スコープの開始・終了タイミングも定義している。
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 | <?xml version="1.0" encoding="UTF-8"?> <pages xmlns="http://jboss.com/products/seam/pages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"> <!-- 入力画面から確認画面への遷移 --> <page view-id="/input.xhtml"> <!-- 確認画面に遷移する直前に、会話スコープを開始 --> <!-- 既に会話スコープを開始している場合は、その会話スコープをそのまま使う --> <begin-conversation join="true"/> <navigation from-action="#{inputFormAction.confirm()}"> <rule if-outcome="confirm"> <redirect view-id="/confirm.xhtml"/> </rule> </navigation> </page> <!-- 確認画面から完了画面・入力画面への遷移 --> <page view-id="/confirm.xhtml"> <navigation from-action="#{inputFormAction.send()}"> <rule if-outcome="send"> <!-- 完了画面にリダイレクト遷移する直前に、会話スコープを終了 --> <end-conversation before-redirect="true"/> <redirect view-id="/complete.xhtml"/> </rule> </navigation> <navigation from-action="#{inputFormAction.back()}"> <rule if-outcome="back"> <redirect view-id="/input.xhtml"/> </rule> </navigation> </page> <!-- 以下はSeamプロジェクト作成時に記載された内容となる --> <exception class="org.jboss.seam.framework.EntityNotFoundException"> <redirect view-id="/error.xhtml"> <message severity="warn">Record not found</message> </redirect> </exception> <exception class="javax.persistence.EntityNotFoundException"> <redirect view-id="/error.xhtml"> <message severity="warn">Record not found</message> </redirect> </exception> <exception class="javax.persistence.EntityExistsException"> <redirect view-id="/error.xhtml"> <message severity="warn">Duplicate record</message> </redirect> </exception> <exception class="javax.persistence.OptimisticLockException"> <end-conversation/> <redirect view-id="/error.xhtml"> <message severity="warn"> Another user changed the same data, please try again </message> </redirect> </exception> <exception class="org.jboss.seam.security.AuthorizationException"> <redirect view-id="/error.xhtml"> <message severity="error"> You don't have permission to access this resource </message> </redirect> </exception> <exception class="org.jboss.seam.security.NotLoggedInException"> <redirect view-id="/login.xhtml"> <message severity="warn"> #{messages['org.jboss.seam.NotLoggedIn']} </message> </redirect> </exception> <exception class="javax.faces.application.ViewExpiredException"> <redirect view-id="/error.xhtml"> <message severity="warn"> Your session has timed out, please try again </message> </redirect> </exception> <exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace"> <http-error error-code="503" /> </exception> <exception> <redirect view-id="/error.xhtml"> <message severity="error">Unexpected error, please try again</message> </redirect> </exception> </pages> |
その他、確認画面(confirm.xhtml)で読んでいるCSSファイル、共通ユーティリティクラスの内容は、以下の記事と同じ内容となっている。
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/seam-web-somescreen/demoSeam
サンプルプログラムの実行結果
サンプルプログラムの実行結果は以下の通りで、画面遷移が行えることが確認できる。
1) JBoss ASサーバーを再起動し、「http://localhost:8082/demoSeam/」とアクセスすると、以下の画面が起動することが確認できる。
2) 以下のように、データを指定し「確認」ボタンを押下すると、確認画面に入力画面の内容が表示される。さらに「戻る」ボタンを押下すると、入力画面に戻り入力値が復旧されることが確認できる。
3) 以下のように、メモを入力しないで「確認」ボタンを押下した場合も、確認画面に入力画面の内容が表示される。さらに「送信」ボタンを押下すると、完了画面に遷移することが確認できる。
4) 2)3)の動作で、以下のように、コンソールに赤枠のログが出力されることが確認できる。
要点まとめ
- JBoss Seamアプリケーションの画面遷移定義は、pages.xmlで指定する。
- JBoss Seamアプリケーションのバッキングビーンには、クラスの先頭に、seamの@Nameアノテーションを付与する必要がある。また、スコープの指定は、クラスの先頭に、seamの@Scopeアノテーションを付与することで行う。
- JBoss Seamアプリケーションで会話スコープを利用するには、バッキングビーンでスコープ指定をすると共に、pages.xmlに、会話スコープの開始・終了タイミングを指定する必要がある。