27:ぜひ聞いてみてください。</textarea>
28: </td>
29: </tr>
30: </table>
31: </div>
32: </form>
リスト7の太字部分をXMLから出力します。それ以外はリスト7の内容を、そのまま
PrintWriterオブジェクトに出力するだけです。
383: } else if (_mode.equals("modify_form")) { 384: // 更新フォームの表示
385: try {
386: // XML の作成
387: // DocumentBuilderFactory オブジェクトの作成 388: DocumentBuilderFactory dbf =
389: DocumentBuilderFactory.newInstance();
390: // DocumentBuilder オブジェクトの作成
391: DocumentBuilder db = dbf.newDocumentBuilder();
392: // XML の読み込み
393: Document document = db.parse(new File(XML_FILE));
394: // BBS 要素の取得
395: Element root = (Element)document.getDocumentElement();
396: // Message 要素一覧の取得
397: NodeList list = root.getChildNodes();
398: for (int i = 0; i < list.getLength(); i++) {
399: if (list.item(i).getNodeType() == Node.ELEMENT_NODE) { 400: Element message = (Element)list.item(i);
401: if (message.getAttribute("No").equals(_no)) { 402: // Subject 要素の取得
403: Node subject =
404: message.getElementsByTagName("Subject").item(0);
405: _subject = subject.getFirstChild().getNodeValue();
406: // Name 要素の取得 407: Node name =
408: message.getElementsByTagName("Name").item(0);
409: _name = name.getFirstChild().getNodeValue();
410: // Comment 要素の取得 411: Node comment =
412: message.getElementsByTagName("Comment").item(0);
413: _comment = comment.getFirstChild().getNodeValue();
414: break;
415: } 416: } 417: }
418: } catch (Exception e) {
419: // 例外処理(メッセージの出力)
420: OutputMessage(out, e.getMessage());
421: return;
422: }
423: out.println("<form action='/bbs/BBS' method='post'>");
424: out.println("<div style='margin-left:40'>");
425: out.println("<input type='hidden' name='mode' value='modify'>");
426: out.println("<input type='hidden' name='no' value='" + _no + "'>");
427: out.println("<table>");
428: out.println("<tr>");
429: out.println("<td><b>題名</b></td>");
430: out.println("<td>");
431: out.println("<input type='text' name='subject' value='" + 432: _subject + "' size='30' maxlength='100' tabindex='1'>");
433: out.println("</td>");
434: out.println("</tr>");
435: out.println("<tr>");
436: out.println("<td><b>名前</b></td>");
437: out.println("<td>");
438: out.println("<input type='text' name='name' value='" + 439: _name + "' size='30' maxlength='100' tabindex='2'>");
440: out.println("<input type='submit' value='更新する' tabindex='4'>");
441: out.println("<input type='reset' value='リセット' tabindex='5'>");
442: out.println("</td>");
443: out.println("</tr>");
444: out.println("<tr>");
445: out.println("<td colspan=2>");
446: out.println("<b>コメント</b><br>");
447: out.println("<textarea name='comment' cols='50' rows='8'"
448: + " tabindex='3'>" + _comment + "</textarea>");
449: out.println("</td>");
450: out.println("</tr>");
451: out.println("</table>");
452: out.println("</div>");
453: out.println("</form>");
⑦と同様に、NoパラメータのMessage要素を検索し、その内容を初期値として表示し ます。
⑬ 検索フォームの表示
メッセージの検索フォームの表示(mode=search_form)の処理です。検索のための HTMLフォームを表示します。
リスト8に検索フォームのHTMLを示します。実際の画面は図3のようになります。
リスト8 検索フォームのHTML
1: <form action='/bbs/BBS' method='post'>
2: <div style='margin-left:40'>
3: <input type='hidden' name='mode' value='search'>
4: <b>キーワード</b>
5: <input type='text' name='keyword' size='30' maxlength='30'>
6: <input type='submit' value='検索する'>
7: </div>
8: </form>
リスト8の内容を、そのままPrintWriterオブジェクトに出力するだけです。
454: } else if (_mode.equals("search_form")) { 455: // 検索フォームの表示
456: out.println("<form action='/bbs/BBS' method='post'>");
457: out.println("<div style='margin-left:40'>");
458: out.println("<input type='hidden' name='mode' value='search'>");
459: out.println("<b>キーワード</b>");
460: out.println("<input type='text' name='keyword' size='30'" + 461: " maxlength='30'>");
462: out.println("<input type='submit' value='検索する'>");
463: out.println("</div>");
464: out.println("</form>");
465: }
⑭ 共通フッタ
468: out.println("</body>");
469: out.println("</html>");
クライアントに表示するHTMLの全ての処理に共通なフッタを出力します。
7.2.3 メッセージ表示
473: private void OutputMessage(PrintWriter out, String msg) { 474: out.println(msg);
475: out.println("</body>");
476: out.println("</html>");
477: }
エラーが発生した場合にメッセージを表示して共通フッタを表示します。
このメソッドは、以下のように、各処理のパラメータのチェック(必要な場合)及び例 外処理で使用しています。(「処理」、「パラメータ」の部分は、それぞれ各処理により異な ります。)
1: } else if (_mode.equals(「処理」)) { 2: // パラメータのチェック
3: if (「パラメータ」== null || 「パラメータ」.length() == 0) { 4: OutputMessage(out, "「パラメータ」を入力してください");
5: return;
6: } 7: try {
8: // 通常の処理 9: } catch (Exception e) {
10: // 例外処理(メッセージの出力)
11: OutputMessage(out, e.getMessage());
12: return;
13: } 14: }
7.3 SAXイベントハンドラ
最後に検索結果表示と一覧表示のSAXイベントハンドラを作成します。
7.3.1 検索結果表示イベントハンドラ
リスト9に検索結果のHTMLを示します。実際の画面は図4のようになります。
リスト9で1~9行目と18~19行目はXMLデータと関連が無い部分ですので、それ ぞれドキュメント開始時とドキュメント終了時に表示します。
要素の開始タグ読み込み時にNo、Delete属性を保存します。そして、要素の終了タグ読 み込み時にテキストデータから Subject、Name、Contribution、Comment 要素を取得し てキーワード検索を行い、マッチした場合に、10~17行目を表示します。
リスト9 検索結果のHTML(太字はXMLデータ)
1: <div align='center'>
2: <table width='80%' border='2'>
3: <tr>
4: <th>番号</th>
5: <th>題名</th>
6: <th>名前</th>
7: <th>日時</th>
8: <th>コメント</th>
9: </tr>
10: <tr>
11: <td>1</td>
12: <td>面白いです。</td>
13: <td>山田太郎</td>
14: <td>2004-11-29 15:28:59</td>
15: <td>タイトル1はとても面白かったです。
16:ぜひ聞いてみてください。</td>
17: </tr>
18: </table>
19: </div>
① クラス定義
480: class SearchHandler extends DefaultHandler { 481:
482: // 出力先を保存するオブジェクト 483: private PrintWriter out;
484: // キーワードを保存する文字列 485: private String keyword;
486: // No 属性値を保存する文字列 487: private String no;
488: // Delete 属性値を保存する文字列 489: private String delete;
490: // テキストデータを保存する文字列 491: private String text;
492: // Subject 要素のデータを保存する文字列 493: private String subject;
494: // Name 要素のデータを保存する文字列 495: private String name;
496: // Contribution 要素のデータを保存する文字列 497: private String contribution;
498: // Comment 要素のデータを保存する文字列 499: private String comment;
500:
501: SearchHandler(PrintWriter pw, String s) { 502: out = pw;
503: keyword = s;
504: }
検 索 結 果 表 示 イ ベ ン ト ハ ン ド ラ を SearchHandler と い う 名 称 で 作 成 し ま す 。
PrintWriterオブジェクトと検索キーワードを受け取るために、コンストラクタを定義し
ています。
② ドキュメント開始時
507: public void startDocument() { 508: // テーブルのヘッダを表示
509: out.println("<div align='center'>");
510: out.println("<table width='80%' border='2'>");
511: out.println("<tr>");
512: out.println("<th>番号</th>");
513: out.println("<th>題名</th>");
514: out.println("<th>名前</th>");
515: out.println("<th>日時</th>");
516: out.println("<th>コメント</th>");
517: out.println("</tr>");
518: }
検索結果をテーブルにするためのヘッダを表示します。
③ 要素の開始タグ読み込み時
521: public void startElement(String uri, 522: String localName, 523: String qName,
524: Attributes attributes) { 525: if (qName.equals("Message")) { 526: // No 属性を保存
527: no = attributes.getValue("No");
528: // Delete 属性を保存
529: delete = attributes.getValue("Delete");
530: if (delete == null) delete = "false";
531: }
532: text = "";
533: }
Message 要素の処理をします。属性を検索し、No属性とDelete属性の内容を保存し
ます。Delete 属性が無い場合には"false"にしておきます。又、全ての要素で、テキスト データの読み込みのために文字列を初期化します。
③ テキストデータ読み込み時
536: public void characters(char[] ch, int offset, int length) { 537: text += new String(ch, offset, length);
538: }
String クラスで文字列を取り出し、テキストデータを保存しておきます。要素の内容
に改行がある場合、複数回呼び出されますので、文字列を連結しています。
④ 要素の終了タグ読み込み時
541: public void endElement(String uri, String localName, String qName) { 542: if (qName.equals("Subject")) {
543: // Subject 要素を保存 544: subject = text;
545: } else if (qName.equals("Name")) { 546: // Name 要素を保存
547: name = text;
548: } else if (qName.equals("Contribution")) { 549: // Contribution 要素を保存
550: contribution = text.substring(0, 10) + " " + text.substring(11, 19);
551: } else if (qName.equals("Comment")) { 552: // Comment 要素を保存
553: comment = text;
554: } else if (qName.equals("Message") && delete.equals("false")) { 555: // 削除されていない場合はキーワードを検索
556: if (subject.indexOf(keyword) != -1 ||
557: comment.indexOf(keyword) != -1) { 558: out.println("<tr>");
559: out.println("<td>" + no + "</td>");
560: out.println("<td>" + subject + "</td>");
561: out.println("<td>" + name + "</td>");
562: out.println("<td>" + contribution + "</td>");
563: out.println("<td>" + comment + "</td>");
564: out.println("</tr>");
565: } 566: } 567: }
それぞれの要素の内容を保存しておきます。Message 要素の終了タグの場合、削除され ていなければキーワード検索を行い、マッチしたメッセージを表示します。
⑤ ドキュメント終了時
570: public void endDocument() { 571: // テーブルを閉じる 572: out.println("</table>");
573: out.println("</div>");
574: }
テーブルを終了します。
7.3.2 一覧表示イベントハンドラ
以下の点を除き、検索結果表示イベントハンドラと同様です。
① イベントハンドラの名称がListHandlerである
② コンストラクタにキーワードが渡されない
③ Message要素の終了タグの場合での処理でキーワードとの比較を行わない
8.プログラムのコンパイルと実行
最後に、プログラムのコンパイルと実行です。(プログラムをコンパイルするための環境 設定については、付録を参照してください。)
リスト10 クラス定義とプログラムのコンパイル(太字はコマンド入力)
C:¥java¥bbs¥WEB-INF¥classes> xjc BBS.xsd
C:¥java¥bbs¥WEB-INF¥classes> javac BBSServlet.java
Tomcatを使用してServletを実行するためには、以下のようなディレクトリ構成が必要
です。ルートディレクトリの名前は任意です。ここでは「C:¥java¥bbs」とします。
図8 プログラム実行のためのディレクトリ構成 スキーマ定義の作成
XSLTの作成
プログラムを作成
(Javaプログラミング)
プログラムの実行 (Tomcatによる実行)
classes
web.xml WEB-INF
lib
Servletと関連プログラムのコンパイル済みファイル 必要なjarファイル
アプリケーションの設定ファイル
ルートディレクトリ(C:¥java¥bbs) アプリケーションのルートディレクトリ
通常表示XSLT
(BBS.xsl)
掲示板XMLファイル
(BBS.xml)
掲示板スキーマ定義
(BBS.xsd)
今回は、それぞれのフォルダに以下のファイルを置き、classesフォルダでコンパイルし ます。尚、他のフォルダでコンパイルしてclassesフォルダにclassファイルをコピーして も問題ありませんが、スキーマ定義のコンパイルで出来た階層構造のclassファイルも全て コピーする必要があります。
・ルート BBS.xml、BBS.xsl
・WEB-INF web.xml
・classes BBSServlet.java、BBS.xsd
・lib jaxb-api.jar、jaxb-impl.jar、jaxb-libs.jar、jaxb-xjc.jar、
jax-qname.jar、 namespace.jar、relaxngDatatype.jar、xsdlib.jar
web.xmlの内容をリスト6に示します。
内容は、<servlet>タグの<servlet-class>タグにServletファイル名、<servlet-name>タ グに、キーワードを指定します。<servlet-mapping>タグの<servlet-name>タグは同じキ ーワードを指定して、<url-pattern>タグにアクセスするURLを指定します。
リスト11 web.xml(太字は任意設定値)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<servlet>
<servlet-name>BBS</servlet-name>
<servlet-class>BBSServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BBS</servlet-name>
<url-pattern>/BBS</url-pattern>
</servlet-mapping>
</web-app>
最後に「Tomcat Manager」から図7のように設定して、配備ボタンを押下すれば
「http://localhost:8080(Tomcatのポート番号)/bbs/BBS」で実行出来ます。ここで「WAR ファイル又はディレクトリのURL」には先程のルートディレクトリを指定します。
図9 Tomcat Managerの設定
9.コーディングリスト
コーディングリストをリスト12に示します。
リスト12 BBSServlet.java 1: import java.io.*;
2: import java.math.*;
3: import java.text.*;
4: import java.util.*;
5: import javax.servlet.*;
6: import javax.servlet.http.*;
7: import javax.xml.bind.JAXBContext;
8: import javax.xml.bind.Marshaller;
9: import javax.xml.bind.Unmarshaller;
10: import javax.xml.bind.Validator;
11: import javax.xml.parsers.*;
12: import javax.xml.transform.*;
13: import javax.xml.transform.dom.*;
14: import javax.xml.transform.stream.*;
15: import org.w3c.dom.*;
16: import org.xml.sax.*;
17: import org.xml.sax.helpers.*;
18:
19: // スキーマコンパイラが生成したクラスのインポート
20: import educationproject.bbs.*;
21:
22: public class BBSServlet extends HttpServlet { 23:
24: // XML ファイル名
25: final String XML_FILE = "C:¥¥java¥¥bbs¥¥BBS.xml";
26: // XSLT スタイルシートファイル名
27: final String XSL_FILE = "C:¥¥java¥¥bbs¥¥BBS.xsl";
28:
29: // GET リクエストの処理
30: public void doGet(HttpServletRequest request, 31: HttpServletResponse response)
32: throws IOException, ServletException { 33: // POST リクエストの処理を呼び出す 34: doPost(request, response);
35: } 36:
37: // POST リクエストの処理
38: public void doPost(HttpServletRequest request, 39: HttpServletResponse response)
40: throws IOException, ServletException { 41:
42: // ファイルに保存するためのストリーム 43: FileOutputStream fos = null;
44:
45: // 入力文字コードの指定
46: request.setCharacterEncoding("Shift_JIS");
47: // 出力文字コードの指定
48: response.setContentType("text/html;charset=Shift_JIS");
49: // PrintWriter オブジェクトを取得 50: PrintWriter out = response.getWriter();
51:
52: // XML ファイルが無ければ作成 53: File file = new File(XML_FILE);
54: if (!file.exists()) { 55: try {
56: // JAXBContext オブジェクトの作成 57: JAXBContext jc =
58: JAXBContext.newInstance("educationproject.bbs");
59: // ObjectFactory オブジェクトの作成 60: ObjectFactory of = new ObjectFactory();
61: // BBS 要素の作成
62: BBS bbs = of.createBBS();
63: // ファイルへ書き出し
64: Marshaller m = jc.createMarshaller();
65: m.setProperty(Marshaller.JAXB_ENCODING, "Shift_JIS");
66: m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 67: Boolean.TRUE);
68: fos = new FileOutputStream(XML_FILE);
69: m.marshal(bbs, fos);
70: fos.close();
71: } catch (Exception e) {
72: // 例外処理(メッセージの出力)
73: OutputMessage(out, e.getMessage());
74: return;
75: } 76: } 77:
78: // リクエストパラメータを取得 79: // 処理モード
80: String _mode = request.getParameter("mode");
81: if (_mode == null) _mode = "normal";
82: // メッセージ番号
83: String _no = request.getParameter("no");
84: // 題名
85: String _subject = request.getParameter("subject");
86: // 名前
87: String _name = request.getParameter("name");
88: // コメント
89: String _comment = request.getParameter("comment");
90: // 検索キーワード
91: String _keyword = request.getParameter("keyword");
92:
93: // 共通ヘッダ
94: out.println("<html>");
95: out.println("<head>");
96: out.println("<title>掲示板</title>");
97: out.println("</head>");
98: out.println("<body>");
99: out.println("<div align='center'><h3>掲示板</h3></div>");
100: out.println("<div align='center'>"
101: + "[ <a href='/bbs/BBS?mode=normal'>通常表示</a> ] "