JSP 演習 1
1.演習内容
個別の機能を狙った例題でなく、htmlとJSPスクリプトの機能を組み合わせて、簡単なプログラムを作る演習です。
下に示す格子をtableを使って作ります。行・列の数は可変とします。
中央は通路、その両側を座席に見立てます。
この座席を予約したり、予約を取消すアプリを作ります。演習は数回に分けて、最終回はstrutsを使って演習します。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
図 1 座席予約入力画面
画面の構成と流れを図2に示します。
![]() |
図2 画面と処理の流れ
図2の(角が丸くない)四角形は画面を持たない処理です。本来はJSPを使わないで実装する方が望ましいところです。しかし、最初はJSPを使って実装します。角が丸い四角形は画面です。
右上の丸みのある四角形はブラウザからのアクセス開始です。これはプログラムの起動とは異なることに注意して下さい。プログラム起動時の初期化は図2には明示されていません。
この画面構成と流れは、現状のJSPの知識を確認し、確実にする演習のためのものであって、実際のアプリにお薦めのものではありません。
図2のプログラムの構成は、意識的に「画面のJSP」と「画面でないJSP」を分けています。このわけを説明しておきます。
図2のような”無構造のプログラム”では、「画面のJSP」と「画面でないJSP」を分けないで、「画面でないJSP」を「画面のJSP」に「混在」させるのが普通です。これこそが、"無構造のプログラム"に無秩序を持ち込む原因です。
この演習(のシリーズ)は”構造を持つのプログラム”へ脱却する予定です。それを念頭に、ここでも「画面のJSP」と「画面でないJSP」を分けています。そのため、”無構造のプログラム”なのに「画面のJSP」と「画面でないJSP」を分けるという普通でない構成にしています。
次の段階で、この演習の結果をもとに構造を整える演習をします。
(1)初期化
ブラウザからのアクセス開始時、つまりセッション開始時の初期化です。webアプリの場合は、アプリ開始時(アプリ起動時)の初期化も必要です。この演習では、アプリ起動時に初期化するものがないので考慮しません。後の回の演習では必要になりますので、その際に考えます。
(2)主入力
図1は図2の主入力の画面です。
座席をクリックする操作だけです。
未予約席をクリックすると、その席を予約します。
予約済席をクリックするとその予約を取り消して、未予約席にします。
(3)主入力解析
予約か取消かを判断して、それぞれに必要な追加の情報を取得する画面を表示します。
(4)予約者情報入力
予約者に関する情報を入力します。
(5)予約処理
予約情報を保管します。
(6)取消確認
取消の確認をします。
(7)取消処理
予約を取り消します。
2.画面概要
予約者情報入力画面
名前 |
|
String 0|1 |
性別 |
|
|
送信 |
|
取消確認画面
次の席の予約を取り消します。
A1
(OKしか選択できません)
OK |
3.実装
3.1 共通の定数
共通に使う変数を定義します。Javaアプリと同じようにstaticなフィールドを使うことにします。
public class GridCnst { // 予約席を保存するMapオブジェクトに対するキー(applicationスコープ) public static final String RESERVED_SEATS = "reservedSeats"; // 席の行数 public static final int ROWS = 4; // 席の列数 public static final int COLS = 4; // 通路より左の席の列数 public static final int COLS1 = 2; // 予約席の表示に使うイメージのCSSクラス public static final String CSS_RESERVED = "reserved"; // 未予約席の表示に使うイメージのCSSクラス public static final String CSS_UNRESERVED = "unreserved"; // 予約席の表示に使うイメージ 1px * 1px public static final String IMG_RESERVED = "../../images/1x1-red.gif"; // 未約席の表示に使うイメージ 1px * 1px public static final String IMG_UNRESERVED = "../../images/1x1-yellow.gif"; // 処理中の(主入力画面でクリックされた)席のId, 例 A1に対するキー(sessionスコープ) public static final String KEY_CUURENT_SEAT_ID = "current-seat-id"; } |
3.2 初期化
step1
画面を持たないJSPに共通のテンプレートを決めます。画面を持たないのでDoctypeの宣言やContentTypeの宣言、<html>~</html>が不要です。
<%@page pageEncoding="UTF-8"%> <%-- 画面を持たないJSPに共通 --%> <%@page import="com.example.grid2.GridCnst" %> <%@page import="java.util.HashMap"%> <%-- <!DOCTYPE...> 不要 --%> <%-- <html>~</html> 不要 --%> |
画面を持たないJSPに共通に必要な分は先頭のpageディレクティブだけです。Doctypeやhtmlタグがないことも共通ですので注意して下さい。不要と書いた行は実際にはコードに含めません。
step2
予約状況を(キー,値)=(シートのid,"reserved")の形でMapオブジェクトに保管します。実際のプログラムの場合はDBの結果から作られる、表示に使うオブジェクトと考えればいいでしょう。演習では、このMapオブジェクトに予約/取消の入力を反映させるだけです。
本例題では、(キー,値)の値は使っていません。キーの値に関係なく、固定値"reserved"を使っています。本来は、席の情報を保持したbeanにしたいところです。
ブラウザからこのJSPに入ってくると、予約状況を保管するMapオブジェクトを生成してsessionに登録します。演習では既存のデータの有無はチェックしません。
<%@page import="java.util.HashMap"%> <%@page import="com.example.grid2.GridCnst"%> <% if(session.getAttribute(GridCnst.RESERVED_SEATS)==null){ session.setAttribute(GridCnst.RESERVED_SEATS, new HashMap<String,String>()); } %> |
Mapオブジェクトを生成すると、主入力画面にフォワードします。
<jsp:forward page="./grid2-top1.jsp"/> |
3.3 主入力画面
step1
図1を実装します。
まず、1座席が表示できるプロトタイプをhtmlで作成します。htmlのレベルで確認しておくと、後のステップが楽になります。
<table class="seat"> <tr> <td><input type="image" src="../../images/1x1-yellow.gif" name="A1" class="unreserved"/></td> <tr/> </table> |
1x1-yellow.gifは縦横1pxのgifファイルです。Windowsの「ペイント」で簡単に作成できます。
tableのcssクラスseatは、境界の表示方法を設定します。
inputのcssクラスunreservedで、セルのサイズを指定します。また、パディングとマージンを0に設定します(*1)。
注*1 FFの場合、パディングとマージンを0にしても、input要素と境界と間に背景色が現れますので別途対策が必要です(本資料では対策していません)。IE6.0の場合は、input要素と境界は密着します。
style
table.seat{ border-collapse:collapse; } table.seat td{ width:3em; height:2em; padding:0; margin:0; border:1px solid black; } table.seat td input.reserved{ width:3em; height:2em; padding:0; margin:0; } table.seat td input.unreserved{ width:3em; height:2em; padding:0; margin:0; } table.seat td.aisle{ padding:0; margin:0; background-color:gray; } |
イメージ ダウンロード
|
1x1-yellow.gif 1x1-red.gif |
|
step2
テーブルを作ります。ループを使って各セルの予約状況を確認します。
<table class="seat"> <% int row,col; String seatId; Map<String,String> reserved = (Map)session.getAttribute(GridCnst.RESERVED_SEATS); for(row=1;row<=GridCnst.ROWS;row++){ %> <tr> ...(後で説明) </tr> <% } %> </table> |
step3
sessionから取得した予約状況を保存しているMapオブジェクト(reserved)を使います。
input要素の次の属性が予約済のセルと未予約のセルで異なります。
# |
属性 |
予約済 |
未予約 |
1 |
イメージ |
IMG_RESERVED |
IMG_UNRESERVERD |
2 |
cssクラス |
CSS_RESERVED |
CSS_UNRESERVED |
さらに、各セルごとに異なる属性として、input要素のname属性があります。これは予約済か未予約かに関係せず、セルの行と列によって決まります。ここでは、表計算の「A1」形式の命名法に基づいてname値を決めています。列番号をアルファベットに変換する処理をメソッドにしておくと便利です。
<%! private String intToABC(int k){ // 0,1,2... をA,B,C...に変換する。 return String.valueOf((char)('A' + k)); } %> |
予約済と未予約で設定する属性値を変える処理は次の通りです。
<% String clazz, img; for(col=0;col<GridCnst.COLS;col++){ seatId = intToABC(col) + row; if(reserved.get(seatId)==null){ clazz = GridCnst.CSS_UNRESERVED; img = GridCnst.IMG_UNRESERVED; }else{ clazz = GridCnst.CSS_RESERVED; img = GridCnst.IMG_RESERVED; } %> |
これらの変数を使うとセルは次のコードで表現できます。
<td><input type="image" src="<%= img %>" name="<%=seatId %>" class="<%=clazz%>"/></td> |
step5
formタグを入れます。
tableの中はinput要素がありますのでformタグで囲います。
<form method="POST" action="./grid2-top2.jsp"> <table class="seat"> ... </table> </form> |
step6
必須ではありませんが、凡例を入れます。
<table style="border-collapse:collapse"> <tr> <td><img src="<%= GridCnst.IMG_RESERVED %>" style="width:1em;"/></td><td>予約済</td> </tr> <tr> <td><img src="<%= GridCnst.IMG_UNRESERVED %>" style="width:1em;"/></td><td>空席</td> </tr> </table> |
3.4 主入力解析
画面を持たないJSPに共通のテンプレートとして初期化JSP(init-session)を参照して下さい。
step1
予約者情報画面を出すか、取消確認画面を出すかを決めるロジックを記述します。
入力(クリック)されたセルが予約済のセルの場合は取消確認画面、未予約のセルの場合は予約者情報画面を出します。行・列についてループして該当するセルがクリックされたか否かチェックします。チェックされていれば、sessionに保管されている予約状況に照らして、予約済か否か判定します。
<% int row,col; String sCol; String seatId=null; boolean toReserve = false; boolean findF = false; Map<String,String> reserved = (Map)session.getAttribute(GridCnst.RESERVED_SEATS); for(col=0;col<GridCnst.COLS;col++){ sCol = intToABC(col); for(row=1;row<=GridCnst.ROWS;row++){ seatId = sCol + row; if(request.getParameter(seatId + ".x")!=null){ findF = true; if(reserved.containsKey(seatId)){ toReserve = false; }else{ toReserve = true; } break; } } if(findF) break; } ... |
step2
toReserveがtrueなら予約、falseなら取消です。これに基づいて次のforwardアクションを書きます。実際にフォワードする前に、選択された席のId(seatId)をsessionに保存します。
<% ...step1参照 session.setAttribute(GridCnst.KEY_CUURENT_SEAT_ID,seatId); if(toReserve){ %> <jsp:forward page="./grid2-reserve1.jsp"/> <% }else{ %> <jsp:forward page="./grid2-unreserve1.jsp"/> <% } %> |
3.5 予約者情報入力画面
画面は動的な情報を含まないので、htmlで作成するのと同じです。画面概要を参考に作成します。
<form method="POST" action="./grid2-reserve2.jsp"> <table> <tr> <td>名前</td> <td><input type="text" name="name"></td> <td>String</td> </tr> <tr> <td>性別</td> <td><input type="text" name="gender"></td> <td>0 | 1</td> </tr> </table> <input type="submit" value="送信"> </form> |
3.6 予約処理
step1
実際のアプリではDBに登録するなどの予約処理をするべきところです。しかし、本例題はJSPが課題なので、DBの処理は略します。予約状況を更新します。
<% String seatId = (String)session.getAttribute(GridCnst.KEY_CUURENT_SEAT_ID); Map<String,String> reserved = (Map<String,String>)session.getAttribute(GridCnst.RESERVED_SEATS); reserved.put(seatId, "reserved"); %> |
step2
主入力画面にforwardします。
<jsp:forward page="./grid2-top1.jsp"/> |
3.7 取消確認画面
画面は動的な情報を含まないので、htmlで作成するのと同じです。画面概要を参考に作成します。
<p> 次の席の予約を取り消します。<br/> <%=session.getAttribute(GridCnst.KEY_CUURENT_SEAT_ID)%><br/> (OKしか選択できません) </p> <form method="POST" action="./grid2-unreserve2.jsp"> <input type="submit" value="OK"> </form> |
3.8 取消処理
step1
予約処理と同じく、実際のアプリではDBに登録するなどの予約処理をするべきところです。しかし、本例題はJSPが課題なので、DBの処理は略します。予約状況を更新します。
<% String seatId = (String)session.getAttribute(GridCnst.KEY_CUURENT_SEAT_ID); Map<String,String> reserved = (Map)session.getAttribute(GridCnst.RESERVED_SEATS); reserved.remove(seatId); %> |
step2
予約処理のstep2と同じです。
以上