ここまでで設計について説明が終わりました。次に、具体的な実装と配備について見ていきましょう。セキュリティ対策として、SQLインジェクション対策などが必要であることを意識しながら実装しましょう。
$CATALINA_HOME/webapps/questionnaireディレクトリを作成し、その配下に図4のようなファイルを用意します。web.xmlとJSPファイル以外は、Standard Taglib 1.0.3に含まれているものを用いています。このほか、TomcatからDBMSを使えるようにJDBCドライバを配置したり、DBMS上に設計したデータベースとテーブルを作成しておくようにしてください。なお本稿では、DBMSへの接続に当たっては、データベースドライバを直接指定する方法を用い、ユーザー名dbq_userとパスワードdbq_passを使うようにしています(実運用のためのアプリケーションでは、DBMSへの接続にはJNDIを利用すべきです)。
今回作成するファイルをアーカイブにしたものを用意しました。Standard Taglibやデータベースドライバのライブラリは含まれていないので、そのままでは動作しませんのでご注意ください。ここからquestionnaire.warをダウンロードしてください。
web.xmlは、リスト2のとおりになります。Standard Taglibを使用するために、taglibタグを使っています。今回は、JSTLのコア機能(core)と、書式(フォーマット)の機能(fmt)と、SQLの機能(sql)を利用しています。taglib-uriタグには、タグライブラリに関連付けられているURIを指定し、taglib-locationタグには、タグライブラリのTLD(Tag Library Discripter)ファイルの位置を指定します。TLDファイルとは、タグライブラリに関する情報を記述したXML文書ファイルのことです。タグライブラリのバージョン情報や使用可能なアクションについての記述がされています。
taglib-uriタグの指定については、JSTL標準を使うので、必ずリスト2のとおりになります。taglib-locationタグの指定については、TLDファイルをどこに置くかによって変わります。このほかの機能を使う場合には、standard-examplesに含まれるweb.xmlを参考にしてください。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'> <web-app> <display-name>/questionnaire</display-name> <description>Questionnaire</description> <session-config> <session-timeout>30</session-timeout> </session-config> <taglib> <taglib-uri>http://java.sun.com/jstl/core</taglib-uri> <taglib-location>/WEB-INF/c.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri> <taglib-location>/WEB-INF/fmt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri> <taglib-location>/WEB-INF/sql.tld</taglib-location> </taglib> </web-app>
COLUMN
JSPは、リスト3のようにXML文書として記述することができます。jsp:rootタグのアトリビュートでStandard TaglibrariesのCoreタグを、cという接頭辞を使って使用すると宣言しています。JSPコンテナはカスタムタグの接頭辞からタグライブラリのURIを取得し、そのURIをキーとしてweb.xmlのtaglibタグを取り出し、TLDファイルの場所を知ることができるような仕組みになっているのです。
ただし、XML文書として記述する場合には注意が必要です。Tomcat5.0.7のアルファ版では修正されていますが、記事執筆時点での最新版Tomcat 4.1.27では、日本語を用いると文字化けが発生します。調べたところ、Tomcatが内部的にXMLのJSPをServeltへ変換するときに問題が発生しています。回避策としては、リソースバンドルを使う、JSTLのURLタグにあるimportタグを使う、文字コードで指定する、といった方法が考えられます。国際化を意識すると、リソースバンドルを利用するのが一番妥当でしょう。もちろん、Tomcatのソースコードを修正するという方法もあります。興味のある人は、jasper/src/share/org/apache/jasper/compiler/ParserController.javaを調べてみましょう。記事執筆時点で、4.0.6と4.1.27のバグとしてパッチを付けてJakartaプロジェクトへ報告しておきましたが、現時点では修正されたバージョンがリリースされるかどうかは不明です。
参考までに、文字コードで指定する場合には、Java2 SDK付属のnative2asciiツールなどでJSPファイルを変換した後、\uXXXXをXXXX;と直します。例えば、「入力」は、「\u5165\u529b」と変換されるますが、それを「入力」と直せば良いのです。
<?xml version="1.0" encoding="Windows-31J"?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jstl/core" version="1.2"> <jsp:directive.page contentType="text/html; charset=Windows-31J"/> <html> <head> <title>Questionnaire Top page</title> </head> <body bgcolor="#FFFFFF"> <p>XML形式のJSP</p> <p> <c:out value="Questionnaire Top page"/> <a href="input.jsp">入力ペー ジへ</a> </p> </body> </html> </jsp:root>
画面制御ページ(index.jsp)はリスト4のようになります。今回のJSTLの説明はここでほとんど終了してしまいます。本来、このようなページはServletとして作成した方がきめ細かい制御ができるのでよいのですが、今回のようにプロトタイプを作成する場合は、大まかな処理でよいので、JSP+JSTLでも十分でしょう。
このページでは、コマンドを受け付けて対応する画面へforwardします。使用するタグの宣言を2〜4行目のtaglibディレクティブで指定しています。ここでは、コアタグ、書式タグ、SQLタグを使っています。prefixで接頭辞を指定し、uriでタグライブラリのURIを指定します。
画面の切り替えには、JSPのforwardアクションを使用していますが、どの画面へforwardするかを判定するためには、c:chooseタグを使っています。これはいわゆる選択文になります。HTTPパラメータのcmdの値によって切り替えをしています。
リスト4を見ると、${と}で囲まれた文字列があることに気付くでしょう。ここで指定されているのは、Expression Language(EL、式言語)の式です。この中に記述されている文字列は、JSTLで利用できる変数や式になります。ELに関する詳細はJSTLの仕様書を参考にしてください。
JSTLでHTTPパラメータを取得するためには、暗黙オブジェクトであるparamを使用します。キーとなるパラメータ名cmdを、param['cmd']のように指定することにより、値を参照することができます。<c:when test="${param['cmd']=='input'}">の部分では、cmdの値がinputという文字列と一致するかを判定しています。これが成立している場合は、c:whenタグのボディ部へ進みます。c:ifタグも条件選択のためのタグです。<c:if test="${dbq == null}">では、変数dbqの値がnullのとき、ボディ部へ進みます。
<%@ page pageEncoding="Windows-31J" contentType="text/html; charset=Windows-31J"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"%> <%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql"%> <fmt:requestEncoding value="Windows-31J"/> <c:if test="${dbq == null}"> <sql:setDataSource driver="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/dbq" user="dbq_user" password="dbq_pass" var="dbq" scope="application"/> </c:if> <c:if test="${dbq == null}"> <jsp:forward page="help.jsp"/> </c:if> <c:choose> <c:when test="${param['cmd']=='input'}"> <jsp:forward page="input.jsp"/> </c:when> <c:when test="${param['cmd']=='confirm'}"> <c:set var="email" value="${param['email']}" scope="session"/> <c:set var="age" value="${param['age']}" scope="session"/> <c:set var="answer" value="${param['answer']}" scope="session"/> <c:if test="${email == '' || age == '' || answer == ''}"> <jsp:forward page="help.jsp"/> </c:if> <jsp:forward page="confirm.jsp"/> </c:when> <c:when test="${param['cmd']=='post'}"> <c:if test="${email == null || age == null || answer == null}"> <jsp:forward page="help.jsp"/> </c:if> <c:if test="${email == '' || age == '' || answer == ''}"> <jsp:forward page="help.jsp"/> </c:if> <sql:transaction dataSource="${dbq}"> <c:set var="maxNo" value="1" scope="page"/> <sql:query var="resultMaxNo"> SELECT max(no) as maxNo FROM tbl_q </sql:query> <c:set var="maxNo" value="${resultMaxNo.rows[0].maxNo}" scope="page"/> <sql:update var="updateCount"> INSERT INTO tbl_q VALUES (?, ?, ?, ?) <sql:param value="${maxNo+1}"/> <sql:param value="${email}"/> <sql:param value="${age}"/> <sql:param value="${answer}"/> </sql:update> </sql:transaction> <sql:query var="tbl_q" dataSource="${dbq}" scope="session"> SELECT * FROM tbl_q where no=? <sql:param value="${maxNo+1}"/> </sql:query> <jsp:forward page="post.jsp"/> </c:when> </c:choose> <jsp:forward page="help.jsp"/>
そのほかのタグについても説明しましょう。
fmt:requestEncodingタグでは、HTTPリクエストに含まれる文字の文字エンコーディングが何かを指定しています。
c:setタグでは、varで指定された変数名の変数へ、valueで指定された値を、有効範囲scopeで設定しています。例えば、<c:set var="a" value="koyama" scope="session"/>とすると、変数aへ、文字列koyamaが、同一セッション内で有効、と設定することになります。
JSTLのSQLタグには、トランザクションをコントロールするためのsql:transactionタグがあります。このタグでは、sql:setDataSourceタグを使ってあらかじめ設定した変数dbqを利用します。sql:transactionタグで囲まれた範囲でトランザクションが行われ、無事処理が終了するとCOMMITが発行され、逆にこの範囲内で処理が失敗した場合には、自動的にROLLBACKが発行されます。
sql:queryタグを使えばデータ検索の実行もできます。データ挿入・データ更新などをするためには、sql:update文を使います。SQLインジェクション対策のため、データベースへの問い合わせでは、例のようにprepared statementを使うようにしましょう。prepared statementを使用する場合のSQLパラメータは、sql:paramタグで指定します。
noの最大値をsql:queryタグを使って取り出している部分を見て分かるように、検索結果は、sql:queryタグのvarで指定した変数に設定されます。変数resultMaxNoは内部的にはjavax.servlet.jsp.jstl.sql.Result型のオブジェクトとなるので、maxNo(select文でmax(no)に別名maxNoを付けている点に注意)の値を取り出したい場合は、式言語では${resultMaxNo.rows[0].maxNo}と指定することになります。
これらのデータベースアクセスの処理を実行するためには、あらかじめデータソースを変数へ設定しておかなければなりません。そのためには、sql:setDataSourceタグを使います。通常はJNDIを利用するのですが、紙面の都合もあり、ここではJDBCドライバを直接利用する方法を紹介しています(JSTLの仕様書ではJNDIを使用することを推奨しています)。driverにはJDBCドライバクラス名を、urlにはデータベースアクセスのためのURLを、userにはユーザー名を、passwordにはパスワードを指定します。これらの情報を使ってデータベースアクセスに成功すると、これ以降は、varで指定されているdbqという変数を使ってデータベースアクセスができるようになります。scopeには変数dbqの有効範囲を指定します。
説明画面(help.jsp)はリスト5のようになります。また、入力画面(input.jsp)はリスト6のようになります。これらは、通常のJSPで記述されています。中身はHTML文書ですが、JSPファイルにしておけば、contentTypeを指定したりpageEncodingを指定することができるため、文字コードの指定などにHTMLのMETAタグを使わなくても済みます。
<%@ page pageEncoding="Windows-31J" contentType="text/html; charset=Windows-31J"%> <html> <head> <title>Questionnaire help page</title> </head> <body bgcolor="#FFFFFF"> <p>JSP+JSTLによるアンケートプログラムのサンプルです。</p> <p><a href="index.jsp?cmd=input">入力ページへ</a></p> </body> </html>
<%@ page pageEncoding="Windows-31J" contentType="text/html; charset=Windows-31J"%> <html> <head> <title>Questionnaire input page</title> <meta http-equiv="Content-Script-type" content="text/javascript"> </head> <body bgcolor="#FFFFFF"> <h3>アンケートとクイズの解答</h3> <p> 問題:Ja-JakartaのホームページのURLはどれでしょう。<br> A. http://www.jajakarta.net/<br> B. http://www.jajakarta.org/<br> C. http://www.jajakarta.jp/ <br> </p> <p> <form name="frm" action="index.jsp" method="post"> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="34%">E-mail:</td> <td width="66%"><input name="email" type="text" size="25"></td> </tr> <tr> <td width="34%">年齢:</td> <td width="66%"><input name="age" type="text" size="3"></td> </tr> <tr> <td width="34%">クイズの答:</td> <td width="66%"> <select name="answer"> <option selected>A</option> <option value="B">B</option> <option value="C">C</option> </select> </td> </tr> </td> </table> <input type="hidden" name="cmd" value="confirm"/> <input type="submit" value="確認"/> </form> </p> </body> </html>
確認画面(confirm.jsp)はリスト7のようになります。c:outタグはvalueに指定された値を出力します。index.jspであらかじめ各変数には値が設定されているので、ここでは、式言語を使って各変数の値を出力するように指定しています。また、クロスサイトスクリプティング対策の1つとして、escapeXmlをtrueとしています。こうすることによって、<や>のようなXMLでは特別な記号を文字として出力することができます。
<%@ page pageEncoding="Windows-31J" contentType="text/html; charset=Windows-31J"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> <html> <head> <title>Questionnaire confirm page</title> </head> <body bgcolor="#FFFFFF"> <h3>アンケート - 入力確認 -</h3> <p> email: <c:out value="${email}" escapeXml="true"/><br> age: <c:out value="${age}" escapeXml="true"/><br> answer: <c:out value="${answer}" escapeXml="true"/><br> </p> <p> <a href="index.jsp?cmd=post">申し込み</a> </p> </body> </html>
終了画面(post.jsp)はリスト8のようになります。ここでは、c:forEachタグを使って、各カラムの値を表示しています。このタグを利用すれば反復処理をさせることができます。
<%@ page pageEncoding="Windows-31J" contentType="text/html; charset=Windows-31J"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> <html> <head> <title>Questionnaire result page</title> </head> <body bgcolor="#FFFFFF"> <h3>アンケート申し込み完了</h3> <p>以下の通りアンケートを受付ました。申し込みありがとうございました。</p> <%-- 投稿結果の表示 --%> <table border="1"> <tr> <c:forEach var="columnName" items="${tbl_q.columnNames}"> <th><c:out value="${columnName}" escapeXml="true"/></th> </c:forEach> </tr> <c:forEach var="row" items="${tbl_q.rowsByIndex}"> <tr> <c:forEach var="column" items="${row}"> <td><c:out value="${column}" escapeXml="true"/></td> </c:forEach> </tr> </c:forEach> </table> <% if (session != null) { session.invalidate(); } %> </body> </html>
以上のファイルを配置し終わったら、Tomcatを再起動して動作確認をしましょう(当然、データベースサーバも起動しておいてください)。http://localhost:8080/questionnaire/index.jspへアクセスして、動作を確認してください。いかがでしょうか?非常に簡単にWebアプリケーションのプロトタイプが作成できることを理解いただけたと思います。
さて、運営側からの要求としては、アンケートの結果を集計して表示する画面なども欲しいところでしょうが、それらについては、読者の課題としたいと思います。ぜひチャレンジしてみてください。
Standard Taglibの使い方について短時間で理解いただけるように説明をしました。タグライブラリを使用すれば、Webアプリケーションのプロトタイプが非常に簡単に作成できることをご理解いただけたでしょうか。しかし、本来はビュー部分をタグベースで記述できることが大きなメリットなので、暗黙オブジェクトや書式タグなどをもっと紹介できれば良かったのですが、別の機会にゆずることにします。これを機会に、ぜひタグライブラリを活用してください。
小山博史(こやま ひろし)
Ja-Jakarta Project運営委員(PMC)。Ja-Jakartaオフィシャルサーバ管理者の1人でもあり、Ja-Jakarta Taglibsサブプロジェクトのコミッタでもある。情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)やbugs(J)の活動へも参加している。書籍出版への貢献としては、Ja-Jakartaプロジェクト監訳の『Jakarta Tomcat エキスパートガイド』(ソフトバンクパブリッシング)、Java FAQ プロジェクトによる『Javaの質問箱』(IDGジャパン)がある。
Ja-Jakarta Projectについて
Ja-Jakartaプロジェクトでは、Jakartaプロジェクトのドキュメントの和訳やプロダクトの国際化/日本語化などを行っている。現在、プロジェクトのメンバーを募集中。Ja-Jakartaプロジェクトの活動に参加しようという方は、「Ja-Jakartaプロジェクトへの参加方法」(http://www.jajakarta.org/site/getinvolved.html)を参照。
Copyright © ITmedia, Inc. All Rights Reserved.