Powered by SmartDoc

Webアプリケーションの方向性

Servlet から JDBC を使う

まずは、JDBCを使うServletを紹介しましょう。基本的には、これまで見てきたServletと同じスタイルです。

JDBCTestServlet.java
import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class JDBCTestServlet extends HttpServlet {

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
                      throws ServletException, IOException {
        doIt(request, response);
    }

    public void doPost(HttpServletRequest request,
                       HttpServletResponse response)
                       throws ServletException, IOException {
        doIt(request, response);
    }

    private void doIt(HttpServletRequest request,
                      HttpServletResponse response)
                      throws ServletException, IOException {

        request.setCharacterEncoding("Shift_JIS");
        response.setContentType("text/html; charset=Shift_JIS");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>JDBCTestServlet</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<p>");
        out.println("タイトルに");
        out.println("Java");
        out.println("という文字列を含む本は");
        out.println("</p>");
        out.println("<p>");

        try {
            Class.forName("org.hsqldb.jdbcDriver");
            String url = "jdbc:hsqldb:hsql://localhost";
            Connection con = DriverManager.getConnection(url, "sa", "");

            String selectStatement =
                "select title " +
                "from books where title like ?";
            PreparedStatement prepStmt =
                con.prepareStatement(selectStatement);
            prepStmt.setString(1, "%" + "Java" + "%");

            ResultSet rs = prepStmt.executeQuery();
            while (rs.next()) {
                String title = rs.getString("title");
                out.println(title);
                out.println("<br>");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        out.println("</p>");
        out.println("</body>");
        out.println("</html>");
    }
}

このServletをコンパイルするには、ServletのAPIとJDBCドライバをclasspathに含める必要があります。この例では、Tomcatに含まれているServletのAPIと、HSQLDBのJDBCドライバが含まれているjarファイルをclasspathに追加しています。

javac -classpath
"%CATALINA_HOME%\common\lib\servlet.jar;%HSQLDB_HOME%\lib\hsqldb.jar"
JDBCTestServlet.java
(実際には1行で入力する)

また、Tomcat上で実行するには、web.xmlにこのServletの情報を追加します。さらに、WEB-INF/libフォルダに、JDBCドライバが含まれているjarファイルを追加する必要があります。

WEB-INF/ --- web.xml
          |- classes/ --- JDBCTestServlet.class
          |- lib/     --- hsqldb.jar (JDBCドライバが含まれている)

ServletとJSPの使い分け

この講義でこれまで見てきたように、ServletとJSPはそれぞれ同じような処理ができます。では、この2つはどのように使い分ければよいのでしょうか。

ServletにしてもJSPにしても、HTMLとプログラムが混在していると、ソースファイルの見通しが悪く保守が大変になります。現在のWebアプリケーション開発では、HTMLの出力部分とプログラムとを分離するのが良いとされています。

JSPは、HTMLにJavaプログラムを埋め込んだようなスタイルです。スクリプトレットは書きにくく、アプリケーションのロジックがわかりにくくなるという欠点があります。また、Webページのデザイナが、スクリプトレットを書くのは大変です。JSPを使うときには、スクリプトレットをJavaBeansとタグライブラリに置き換えて、JSPはHTMLの出力に専念させるのが良いでしょう。

そして、ServletはJavaプログラムなので、HTMLの出力をさせずに、ロジックの処理に専念させるのが良いでしょう。Servletでの処理結果を、JSPで表示することができます。Servletでのプログラミングは、本質的には、HttpServletRequestとHttpServletResponseに働きかけるというものです。Servletは、RequestをResponseに変換するコンポーネントであると考えられます。

MVCモデル2

ソフトウェア開発には、「Model-View-Controller (MVC)」と呼ばれている方法論があります。この方法論では、アプリケーションを構成するコンポーネントが、「モデル(Model)」・「ビュー(View)」・「コントローラ(Controller)」と名づけられた、3つの部分に分けられています(図[MVCモデル2]

MVCモデル2

「モデル」は、アプリケーションのデータとビジネスロジックをまとめた部分です。「ビュー」は、「モデル」の表示を担当する部分です。「コントローラ」は、「モデル」と「ビュー」を制御する部分です。

最近のJ2EEとその関連技術では、モデルにJavaBeansを使い、ビューにJSPを使い、コントローラにServletを使う開発手法が採用されています。このJ2EEの手法は、「MVCモデル2」と呼ばれています。

サンプルアプリケーション

ここでは、サンプルを通して、MVCモデル2によるアプリケーションの基本的な構成について見てみましょう。このサンプルは、本の検索アプリケーションです。

基本的な処理の流れ

このアプリケーションの処理は、まずユーザがWebブラウザに検索したい本の情報を入力し、その情報をもとにServletが処理をして、結果をJSPが表示する、という流れになります。それぞれの部分の役割は、次のようになります。

Webブラウザ
Servlet
JSP

Servletがコントローラとなり、JSPがビューを担当します。また、モデルであるJavaBeansは、アプリケーションで処理するデータを保持して、ServletからJSPに渡されます。

今回のサンプルプログラムでは、コントローラがBookSearchServletであり、ビューがlist.jspで、モデルが示しているBookDataとBookListになります。

モデルとなる JavaBeans

まず、モデルとなるJavaBeansを見てみましょう。BookDataは、1冊の本のデータを表すBeanであり、BookListは、複数のBookDataをリスト化したものです。

package jp.ac.wakhok.library;

public class BookData {

    private String ndc           = "";
    private String tyosya_hyouji = "";
    private String id            = "";
    private String title         = "";
    private String author        = "";
    private String publisher     = "";

    public String getNdc() {
        return ndc;
    }

    public void setNdc(String ndc) {
        this.ndc = ndc;
    }

    public String getTyosya_hyouji() {
        return tyosya_hyouji;
    }

    public void setTyosya_hyouji(String tyosya_hyouji) {
        this.tyosya_hyouji = tyosya_hyouji;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

}
package jp.ac.wakhok.library;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class BookList {

    private List list;

    public BookList() {
        list = new ArrayList();
    }

    public void add(BookData data) {
        list.add(data);
    }

    public Iterator getIterator() {
        return list.iterator();
    }

}

Servlet の働き

次に、コントローラであるServletのソースを見てみましょう。

import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import jp.ac.wakhok.library.*;

public class BookSearchServlet extends HttpServlet {

    private String jspFile = "/list.jsp";
    private ServletContext context;

    public void init() throws ServletException {
        context = getServletContext();
    }

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
                      throws ServletException, IOException {
        doIt(request, response);
    }

    public void doPost(HttpServletRequest request,
                       HttpServletResponse response)
                       throws ServletException, IOException {
        doIt(request, response);
    }

    private void doIt(HttpServletRequest request,
                      HttpServletResponse response)
                      throws ServletException, IOException {
        request.setCharacterEncoding("Shift_JIS");

        String ndc           = request.getParameter("ndc");
        String tyosya_hyouji = request.getParameter("tyosya_hyouji");
        String id            = request.getParameter("id");
        String title         = request.getParameter("title");
        String author        = request.getParameter("author");
        String publisher     = request.getParameter("publisher");
        BookList list =
            search(ndc, tyosya_hyouji, id, title, author, publisher);

        HttpSession session = request.getSession();
        session.setAttribute("bookList", list);

        context.getRequestDispatcher(jspFile)
               .forward(request, response);
    }

    private BookList search(String ndc, String tyosya_hyouji,
                            String id, String title,
                            String author, String publisher) {
        BookList list = new BookList();
        try {
            Class.forName("org.hsqldb.jdbcDriver");
            String url = "jdbc:hsqldb:hsql://localhost";
            Connection con = DriverManager.getConnection(url, "sa", "");

            String selectStatement =
                "select * " +
                "from books where ndc           like ? " +
                             "and tyosya_hyouji like ? " +
                             "and id            like ? " +
                             "and title         like ? " +
                             "and author        like ? " +
                             "and publisher     like ? ";
            PreparedStatement prepStmt =
                con.prepareStatement(selectStatement);
            prepStmt.setString(1, appendPercent(ndc));
            prepStmt.setString(2, appendPercent(tyosya_hyouji));
            prepStmt.setString(3, appendPercent(id));
            prepStmt.setString(4, appendPercent(title));
            prepStmt.setString(5, appendPercent(author));
            prepStmt.setString(6, appendPercent(publisher));

            ResultSet rs = prepStmt.executeQuery();
            while (rs.next()) {
                BookData book = new BookData();
                book.setNdc(rs.getString("ndc"));
                book.setTyosya_hyouji(rs.getString("tyosya_hyouji"));
                book.setId(rs.getString("id"));
                book.setTitle(rs.getString("title"));
                book.setAuthor(rs.getString("author"));
                book.setPublisher(rs.getString("publisher"));
                list.add(book);
            }
            prepStmt.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return list;
    }

    private String appendPercent(String from) {
        StringBuffer to = new StringBuffer();
        to.append("%");
        to.append(from);
        to.append("%");

        return new String(to);
    }
}

このServletでは、まずWebブラウザから入力されたデータを取り出し、JDBCを使って、そのデータを検索しています。そして検索結果を、モデルであるBookListに入れています。

        BookList list =
            search(ndc, tyosya_hyouji, id, title, author, publisher);

そして次の部分で、HttpSession型のインスタンスsessionに、bookListという名前でBeanを登録します。

        HttpSession session = request.getSession();
        session.setAttribute("bookList", list);

Beanの内容の出力はlist.jspというJSPページに任せています。

        context.getRequestDispatcher("/list.jsp");
               .forward(request, response);

JSP とタグライブラリ

最後に、ビューであるJSPのソースを見てみましょう。

<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<jsp:useBean id="bookList"
             class="jp.ac.wakhok.library.BookList"
             scope="session"  />

<html>
<head>
<title>検索結果</title>
</head>
<body>

<h1>検索結果</h1>

<ul>
<c:forEach var="book" items="${bookList.iterator}" >
<li>
<c:out value="${book.title}" />
 / 
<c:out value="${book.author}" />
</li>
</c:forEach>
</ul>

</body>
</html>

まず、Servletの処理で、HttpSessionに登録したBookList Beanの利用を宣言しています。

<jsp:useBean id="bookList"
             class="jp.ac.wakhok.library.BookList"
             scope="session"  />

JSPでは、ユーザが自分でJSP内のタグを定義できます。こうしたタグのことを、「カスタム・タグ」と言います。

カスタム・タグによって、これまでスクリプトレットとして書いていたプログラムを、JSPの要素に閉じ込めることができ、HTMLが見やすくなります。

いくつかの関連する複数のタグは、1つの「タグライブラリ」にまとめられます。ここでは、既存のタグライブラリであるJavaServer Pages Standard Tag Library (JSTL) (http://java.sun.com/products/jsp/jstl/)のサンプルを紹介します。

まず次の部分で、使用するタグライブラリと、その接頭辞を指定します。ここでは、接頭辞に"c"という文字が指定されています。

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

このJSPでは、c:forEachというタグとc:outというタグが使われています。

<c:out value="${book.title}" />

c:outというタグは、値を出力するタグです。valueという属性値で指定したデータを出力します。このタグによって、"<"や"&"といった文字は、エスケープされて出力されます。このことを、タグライブラリを使わずにJSPで実現するには、スクリプトレットを記述する必要があります。

ここでのvalueの値が、

  ${book.title}

という見慣れないスタイルになっています。これは、Beanであるbookのtitleプロパティの値を示します。

JSTLでは、次のような「式言語」と呼ばれる表現が可能になっています。こうしたスタイルは、今後のJSPで正式に取り上げられる予定です。

  ${var}         ... 変数 var
  ${param.title} ... フォームから入力されたデータ "title" の値
  ${book.author} ... book という Bean の author プロパティ

もう一つのc:forEachというタグでは、ループの処理を行っています。

<c:forEach var="book" items="${bookList.iterator}" >
  .....
</c:forEach>

このタグは、items属性で与えられたCollection, Iteratorなどに含まれているオブジェクトを繰り返し処理します。現在処理をしているオブジェクトは、bookという変数に入ります。

BookListにはgetIteratorというメソッドがあるので、JavaBeansの命名規則により、BookListというBeanにはiteratorというプロパティがあると判断されます。このため、${bookList.iterator}では、BookList#iteratorメソッドが適用されます。このメソッドはIteratorを返します。Iteratorには、BookDataの集合が含まれているので、この<c:forEach>タグでは、BookDataをひとつひとつ処理することになります。

スクリプトレットを使わなければ書けなかったループの処理が、タグライブラリを使うことによってタグで書けるようになります。

サンプルアプリケーションを動かす

JSTLを利用するためには、JSTLの配布パッケージのlibフォルダに含まれている次の4つのjarファイルをWEB-INF/libフォルダにコピーします。

このサンプルアプリケーションを動かすには、Tomcatには次のようにファイルを配置します。

test --- input.html
      |- list.jsp
      |- WEB-INF/ ---- web.xml
                   |-- classes/
                   |     |- BookSearchServlet.class
                   |     |- jp/ac/wakhok/library/ --- BookData.class
                   |     |- jp/ac/wakhok/library/ --- BookList.class
                   |-- lib/
                         |- hsqldb.jar
                         |- jstl.jar
                         |- standard.jar
                         |- jaxen-full.jar
                         |- saxpath.jar

Struts

いま見てきた図書検索アプリケーションでは、検索結果をリスト表示するという、単純なものでした。では、このアプリケーションに、1冊の本の詳細を表示する機能を付け加えるとしたら、どのようなプログラムが必要になるでしょうか。

詳細表示用のServletを付け加えるのが、1つの方法です。ただこの方法をとると、Servletが増えるに従って、アプリケーション全体に見通しが悪くなってきます。

こうしたことを解決するため、最近では「Struts」というフレームワークがよく使われています。StrutsはMVCモデル2のビューとコントローラをサポートしており、Webアプリケーションの開発の手間を減らしてくれます。Strutsについては、http://www.ingrid.org/jakarta/struts/を参照してください。