JakartaEE(JavaEE)

JSFプロジェクトで複数画面をもつWebアプリケーションを作成してみた

JSF(JavaServer Faces)プロジェクトを利用して、JavaベースのWebアプリケーションを作成することができる。

今回は、入力画面・確認画面・完了画面の3画面を含み、HTMLオブジェクトとしてテキストボックス・ラジオボタン・チェックボックス等を含むWEBアプリケーションを作成してみたので、そのサンプルプログラムを共有する。

前提条件

下記記事に記載した、JSFプロジェクトの作成が完了していること。

Eclipse上でJSFプロジェクトを作成してみたJakartaEE(旧称:JavaEE)では、JSF(JavaServer Faces)というJavaベースのWebアプリケーションフレ...

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

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

web.xmlの内容は以下の通りで、初期表示画面をinput.xhtmlに設定し、テキスト・テキストエリアの文字化け防止用Filterを追加している。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    id="WebApp_ID" version="3.0">
  <display-name>demoJsf</display-name>
  <welcome-file-list>
    <!-- 画面遷移先でJSFタグを利用できるよう、Faces ServletのURL(faces/)を先頭に付与 -->
    <welcome-file>faces/input.xhtml</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <!-- JSFでのテキスト・テキストエリアの文字化け防止用Filterを設定 -->
  <filter>
    <filter-name>Encoding</filter-name>
    <filter-class>common.EncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>Encoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

また、テキスト・テキストエリアの文字化け防止用Filterの内容は、以下の通り。

package common;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 文字コードをweb.xmlに記載したものに設定するためのフィルタ.
 */
public class EncodingFilter implements Filter {

    /** 文字コード */
    private String encoding;

    @Override
    public void doFilter(ServletRequest request
          , ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");
    }

    @Override
    public void destroy() {}

}

さらに、入力画面(input.xhtml)、確認画面(confirm.xhtml)、完了画面(complete.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:head>
<h: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>
</h:body>
</html>
<?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>
   <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><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()}" />&nbsp;
     <h:commandButton value="戻る" action="#{inputFormAction.back()}" />
   </h:form>
</h:body>
</html>
<?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>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
   <title>完了画面</title>
</h:head>
<h:body>
   <p>お申し込みが完了しました。</p>
</h:body>
</html>

また、各画面のForm値と画面遷移処理を定義したクラスの内容は以下の通りで、入力画面・確認画面から呼び出されている。

package faces;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.enterprise.context.SessionScoped;
import javax.faces.model.SelectItem;
import javax.inject.Named;

import common.CommonUtil;

/**
 * 画面のフォーム値と画面遷移メソッドを定義.
 */
// @Namedアノテーションは、JSFのXHTMLファイルから#{inputFormAction}で
// Javaクラスを参照できるようにしている(→バッキングビーン)
// @SessionScopedアノテーションは、このバッキングビーンの生存期間を
// セッションに設定している
@Named(value="inputFormAction")
@SessionScoped
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 String 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());

        // 確認画面に遷移、ただし、遷移先URLを正しく表示し画面遷移を安定させるため、
        // ?faces-redirect=trueを付与し、フォワードからリダイレクトに変更している
        return "confirm.xhtml?faces-redirect=true";
    }

    /**
     * 入力画面に戻る.
     * @return 入力画面へのパス
     */
    public String back(){
    	// 入力画面に戻る
        return "input.xhtml?faces-redirect=true";
    }

    /**
     * 完了画面への遷移.
     * @return 完了画面へのパス
     */
    public String send(){
    	// 確認画面に表示された値を出力
        System.out.println(this.toString());

        // 完了画面に遷移
        return "complete.xhtml?faces-redirect=true";
    }

    @Override
    public String toString() {
        return "InputForm [name=" + name + ", birthYear=" + birthYear 
             + ", birthMonth=" + birthMonth + ", birthDay=" + birthDay 
             + ", sex=" + sex + " sexLabel=" + sexLabel 
             +  ", memo=" + memo + ", checked=" + checked + "]";
    }

    // 以下はGetter/Setterメソッド
    // JSFのXHTMLファイルから各メンバ変数にアクセスするために必要
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBirthYear() {
        return birthYear;
    }

    public void setBirthYear(String birthYear) {
        this.birthYear = birthYear;
    }

    public String getBirthMonth() {
        return birthMonth;
    }

    public void setBirthMonth(String birthMonth) {
        this.birthMonth = birthMonth;
    }

    public String getBirthDay() {
        return birthDay;
    }

    public void setBirthDay(String birthDay) {
        this.birthDay = birthDay;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getSexLabel() {
        return sexLabel;
    }

    public void setSexLabel(String sexLabel) {
        this.sexLabel = sexLabel;
    }

    public String getMemo() {
        return memo;
    }

    public void setMemo(String memo) {
        this.memo = memo;
    }

    public String getChecked() {
        return checked;
    }

    public void setChecked(String checked) {
        this.checked = checked;
    }

    public List<SelectItem> getBirthMonthItems() {
        return birthMonthItems;
    }

    public void setBirthMonthItems(List<SelectItem> birthMonthItems) {
        this.birthMonthItems = birthMonthItems;
    }

    public List<SelectItem> getBirthDayItems() {
        return birthDayItems;
    }

    public void setBirthDayItems(List<SelectItem> birthDayItems) {
        this.birthDayItems = birthDayItems;
    }

    public List<SelectItem> getSexItems() {
        return sexItems;
    }

    public void setSexItems(List<SelectItem> sexItems) {
        this.sexItems = sexItems;
    }

}

さらに、確認画面(confirm.xhtml)で読んでいるCSSファイル、共通ユーティリティクラスの内容は、以下の通り。

.lineBreakFormat{
    /* 改行コードをbrタグに変換し表示する */
	/* 空白文字はそのまま残す */
    white-space: pre;
}
package common;

/**
 * 共通ユーティリティクラス.
 */
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();
    }
}

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/javaee-jsf-web-somescreen/demoJsf



「EaseUS Partition Master」はパーティション分割・結合・作成・サイズ変更等を直感的に行える便利ツールだったハードディスクの記憶領域を論理的に分割し、分割された個々の領域のことを、パーティションといいます。 例えば、以下の図の場合、C/D...

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

サンプルプログラムの実行結果は以下の通り。

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

2) 以下のように、データを指定し「確認」ボタンを押下すると、確認画面に入力画面の内容が表示される。さらに「戻る」ボタンを押下すると、入力画面に戻り入力値が復旧されることが確認できる。
サンプルプログラムの実行結果_2_1

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

3) 以下のように、メモを入力しないで「確認」ボタンを押下した場合も、確認画面に入力画面の内容が表示される。さらに「送信」ボタンを押下すると、完了画面に遷移することが確認できる。
サンプルプログラムの実行結果_3_1

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

4) 2)3)の動作で、以下のように、コンソールに赤枠のログが出力されることが確認できる。
サンプルプログラムの実行結果_4

要点まとめ

  • JSF(JavaServer Faces)プロジェクトを利用して、JavaベースのWebアプリケーションを作成することができる。
  • JSFのXHTMLファイルからJavaクラスやメンバ変数を参照できるようにする(→バッキングビーンの生成)には、Javaクラスに@Namedアノテーションを付与し、メンバ変数のGetter/Setterメソッドを付与する必要がある。
  • JSFのXHTMLファイルからバッキングビーンを参照するには、#{バッキングビーン名.メンバ変数}、#{バッキングビーン名.メソッド名()}というEL式を利用する。
  • JSFのテキスト/テキストエリアの文字化けを防止するには、文字化け防止用のFilterを追加する。