一覧画面とエラー画面の作成

次に、一覧画面とエラー画面を実装します。

修正内容は以下の通りです。

ファイル 修正・追加内容
/WEB-INF/classes/test/TodoServlet.class action=login_actionの場合の処理とデータベース処理を追加。
データベースから得た情報は後述するtest.User, test.Itemオブジェクトに格納し、
HttpServletRequestオブジェクトを経由してJSPファイルに渡す。
/WEB-INF/classes/test/User.class TODO_USERテーブルのデータを格納するクラスを作成。
カラムごとにset(カラム名)メソッドとget(カラム名)メソッドを定義することでServletで値を設定、
JSPファイルから値を取得できるようにする。
/WEB-INF/classes/test/Item.class TODO_ITEMテーブルのデータを格納するクラスを作成。
/WEB-INF/jsp/list.jsp 一覧画面を表現するJSPファイルを作成。
HttpServletRequestオブジェクトからUser,Itemクラスのインスタンスを取得し、画面に反映する。


H2 Databaseライブラリのクラスパスへの追加

WEBアプリケーションからJARファイル中のクラスを参照するためには、JARファイルをWEB-INF/lib以下に配置する必要があります。
H2 DatabaseライブラリをWEBアプリケーションから参照できるようにしましょう。

  1. ログイン処理および一覧画面はデータベースアクセスを必要とするため、 まず、WEB-INF/libにH2 DatabaseのJARをコピーします。

    C:\Program Files\H2\bin\h2-1.1.108.jarを、 (todotestプロジェクト)/WEB-INF/libにコピーします。

  2. 次に、todotestプロジェクトのクラスパスにH2 DatabaseのJARを登録します。
    todotestプロジェクト上で右クリックし、「Properties」を選択します。

  3. ダイアログが表示されたら、「Java Build Path」の「Libraries」タブを選択します。

    Propertiesダイアログ

  4. 「Add JARS...」ボタンを押し、「(todotestプロジェクト)/WEB-INF/lib/h2-1.1.108.jar」を選択します。
    これでWEBアプリケーションからH2 Databaseのクラスを参照できるようになります。

    JAR Selectionダイアログ

Javaクラスの記述

次に、Javaクラスを作成していきましょう。

  1. まず、データベースのデータを格納するための値オブジェクトを作成します。

    ダウンロード User.java Item.java

    これらのクラスには、テーブルのカラムごとに以下のようなメンバ変数、メソッドを実装します。

    13~16行目:
     13: /**
     14:  * 名前を保持します。
     15:  */
     16: private String _name;

    値を格納するためのメンバ変数の宣言。
    44~50行目:
     44: /**
     45:  * 名前を取得します。
     46:  * @return
     47:  */
     48: public String getName() {
     49:     return _name;
     50: }

    値を取得するためのメソッドの宣言。
    JSPファイルはこのメソッドを呼び出すことで、HTMLに出力すべき値を取得します。
    52~58行目:
     52: /**
     53:  * 名前を設定します。
     54:  * @param name
     55:  */
     56: public void setName(String name) {
     57:     _name = name;
     58: }

    値を設定するためのメソッドの宣言。
    Servletファイルはこのメソッドを呼び出すことで、このインスタンスに値を設定します。


  2. 次に、Servletクラスを記述します。
    postメソッド要求時の処理として、actionパラメータがlogin_actionだった場合の処理を記述します。

    ダウンロード TodoServlet.java

    81~98行目:
     81:     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
     82:             throws ServletException, IOException {
     83:         // 要求からactionパラメータを取得
     84:         String action = req.getParameter("action");
     85: 
     86:         String forward = null;
     87:         if ("login_action".equals(action)) {
     88:             // ログイン画面からの入力受付
     89:             forward = doLoginAction(req, resp);
     90:         } else {
     91:             // 不正なアクションの場合
     92:             forward = doError(req, resp, "不正なリクエストです");
     93:         }
     94: 
     95:         // JSPへのフォワード
     96:         RequestDispatcher dispatcher = req.getRequestDispatcher(forward);
     97:         dispatcher.forward(req, resp);
     98:     }

    POSTメソッドの処理の実装。
    login_actionパラメータが要求された場合の処理、 および不正なパラメータが指定された場合の処理を記述します。
    109~138行目:
    109:     private String doLoginAction(HttpServletRequest req,
    110:             HttpServletResponse resp) throws ServletException, IOException {
    111:         String userID = req.getParameter("user_id");
    112:         String password = req.getParameter("password");
    113:         if (userID == null || password == null) {
    114:             throw new ServletException("不正なパラメータです。");
    115:         }
    116: 
    117:         try{
    118:             // ユーザを取得する
    119:             User user = getUser(userID, password);
    120:             if(user == null) {
    121:                 return doError(req, resp, "不正なユーザIDもしくはパラメータです。");
    122:             }
    123:     
    124:             // 名前をセッションに格納する
    125:             req.getSession().setAttribute("currentUser", user);
    126:     
    127:             // アイテムを取得する
    128:             Item[] items = getItems();
    129:     
    130:             // アイテムを要求オブジェクトに格納する
    131:             req.setAttribute("items", items);
    132:     
    133:             // 一覧を表示する
    134:             return JSP_BASE + "list.jsp";
    135:         }catch(SQLException e) {
    136:             return doError(req, resp, e.getMessage());
    137:         }
    138:     }

    ログイン処理。
    user_id, passwordパラメータからデータベースの内容を検索し、 ユーザ情報が正しい場合はTODO_ITEMテーブルの内容を取得し、JSPで参照できるようにします。

    ItemオブジェクトはHttpServletRequest.setAttributeによりリクエストオブジェクト内に格納します。
    このメソッドを呼び出すことで、JSPファイル内でHttpServletRequest.getAttributeを呼び出すことでItemオブジェクトを参照できるようになります。
    これは、同一のリクエスト処理中にのみ参照可能です。

    UserオブジェクトはHttpSession.setAttributeメソッドによりセッションオブジェクト内に格納します。
    セッションとはブラウザとサーバ間の一連のやり取りを識別するためのものです。
    セッションオブジェクトにオブジェクトを設定することで、リクエストを発行したブラウザとのやり取りの中で常にオブジェクトを参照可能です。

    ここで設定されたオブジェクトは後述するlist.jspによって参照されます。
    150~156行目:
    150: private String doError(HttpServletRequest req, HttpServletResponse resp,
    151:         String message) throws ServletException, IOException {
    152:     req.setAttribute("message", message);
    153:     
    154:     // エラーを表示する
    155:     return JSP_BASE + "error.jsp";
    156: }

    エラー発生時の処理。
    HttpServletRequest.setAttributeメソッドによりエラーメッセージを設定しています。
    ここで設定されたエラーメッセージは後述するerror.jspによって参照されます。
    163~182行目:
    163: private Connection getConnection()
    164:     throws SQLException {
    165:     // Connectionの準備
    166:     if(_pooledConnection != null) {
    167:         return _pooledConnection;
    168:     }
    169:     try {
    170:         // 下準備
    171:         Class.forName("org.h2.Driver");
    172:         _pooledConnection = DriverManager.getConnection(
    173:                 "jdbc:h2:tcp://localhost/~/test", "sa", "");
    174:         return _pooledConnection;
    175:     } catch (ClassNotFoundException e) {
    176:         _pooledConnection = null;
    177:         throw new SQLException(e);
    178:     } catch (SQLException e) {
    179:         _pooledConnection = null;
    180:         throw e;
    181:     }
    182: }

    JDBC接続の初期化処理。
    今回は一度JDBCとの接続を確立したらそのオブジェクトを使いまわすような実装としています。
    接続確立は通信処理やオブジェクト生成処理をともなう非常にコストの高い処理であるため、 このように回数を減らす工夫をおこなっています。
    192~219行目:
    192:     private User getUser(String userID, String password)
    193:             throws SQLException {
    194:         Statement statement = null;
    195:         try {
    196:             // SQL文を発行
    197:             statement = getConnection().createStatement();
    198:             ResultSet resultSet = statement
    199:                     .executeQuery("SELECT ID,NAME FROM TODO_USER WHERE ID='"
    200:                             + userID + "' AND PASSWORD='" + password + "'");
    201:             boolean br = resultSet.first();
    202:             if (br == false) {
    203:                 return null;
    204:             }
    205:             User user = new User();
    206:             user.setId(resultSet.getString("ID"));
    207:             user.setName(resultSet.getString("NAME"));
    208: 
    209:             return user;
    210:         }catch(SQLException e) {
    211:             _pooledConnection = null;
    212:             throw e;
    213:         } finally {
    214:             if (statement != null) {
    215:                 statement.close();
    216:                 statement = null;
    217:             }
    218:         }
    219:     }

    ユーザ情報の取得処理。
    取得した内容はUserオブジェクトに格納しています。
    Itemオブジェクトの取得も同様の手順でおこなっています。


JSPファイルの記述

次に、JSPファイルを記述します。
さきほどのログイン画面とは異なり、TodoServletが生成したオブジェクトをHttpServletRequest経由で取得し、画面表示に反映しています。

  1. 先に作成したlist.htmlの拡張子をjspにリネームし、WEB-INF/jspディレクトリにコピーした後、 JSPコードを追加します。

    ダウンロード list.jsp


    以下のようなコードを追加します。

    2~4行目:
      2: <%@ page import="test.User" %>
      3: <%@ page import="test.Item" %>
      4: <%@ page import="java.util.Calendar" %>

    import宣言の記述。
    Javaプログラムと同様、JSPコード内でクラスを参照する場合、import宣言を記述する必要があります。
    5~7行目:
      5: <%
      6:     User currentUser = (User) request.getSession().getAttribute("currentUser");
      7: %>

    Javaプログラムの記述。
    Javaプログラムを記述する場合、<%と%>で囲みます。

    ここでは、HttpServletRequestオブジェクト(変数名requestで参照可能)からセッションオブジェクトを取得し、 getAttributeメソッドを使用して現在ログインしているユーザ情報を取得しています。
    18行目:
     18: ようこそ <%= currentUser.getName() %> さん

    変数の埋め込み。
    変数を埋め込む場合、<%=と%>で囲みます。
    49~57行目:
     49: <%
     50:    Item[] items = (Item[]) request.getAttribute("items");
     51:    if(items.length == 0) {
     52:        // アイテムが存在しない場合
     53: %>
     54: <div align="center">作業項目はありません。</div>
     55: <%
     56:    }else{
     57: %>

    制御構造の記述。
    Javaプログラムと同様に制御構造を設けて、条件文が成立した場合のみ文字列を出力することが可能です。
    forループ、whileループなどについても同様です。
    ここでは、HttpServletRequestオブジェクトからアイテムリストを取得し、その内容に応じてHTMLを生成しています。


  2. 先に作成したerror.htmlの拡張子をjspにリネームし、WEB-INF/jspディレクトリにコピーした後、 JSPコードを追加します。

    ダウンロード error.jsp
    18行目:
     18: 内容: <%= request.getAttribute("message") %>

    変数の埋め込み。
    ここでは、HttpServletRequestオブジェクトからエラーメッセージを取得しています。


ここまでのファイル

ここまででプロジェクトディレクトリの内容は以下のようになります。
正しく作成できているか、確認してください。

ダウンロード todotest_2.zip


デバッグ・動作確認

ブラウザで以下のURLを開き、動作確認をおこないます。
http://localhost:8080/todotest/todo?action=login

TODO_USERテーブルに登録したユーザIDとパスワードでログインし、 以下のような画面が表示されれば成功です。
なお、TODO_ITEMテーブルには、先に作成したItemDBコマンドラインアプリケーションを用いて あらかじめデータを登録しておきましょう。
(作業項目登録画面はこの次で実装します。)

一覧画面