連載
» 2001年07月10日 00時00分 公開

Tomcatを使う「JSPプログラミング」(4):Webプログラミングの前提知識をおさえる

[三谷純,タイムインターメディア]

前回はJSPの基本的なコーディング手法について説明しました。今回はJSPを用いるWebプログラミングについて説明します。インターネットを介してブラウザとサーバが情報のやりとりを行い、一連の処理を行うアプリケーションをWebアプリケーションといいます。ローカルのPCにインストールされるアプリケーションとは対照的に、ユーザーとのインタラクションはHTTPによる通信を介したやりとりになります。今回は、このWebアプリケーションを作成するうえで必要になる知識について解説します。また、JSPでのWebアプリケーションの管理、サーバ側でのJSPの処理の実際についても解説します。


 Webプログラミングとは、ユーザーの使用するブラウザとサーバが対話的な処理を行うWebアプリケーションを作成する作業です。ブラウザからはフォームに入力された文字列などのデータと、次に表示するページの要求(リクエスト)が送られ、サーバからは1ページ1ページのHTMLデータ(レスポンス)が送られます。Webプログラミングのための手法としてJSPを採用した場合でも、Webページの表示に関するHTMLの知識、またブラウザとサーバ間のやりとりに用いられるHTTPプロトコルやWebサーバとして機能するApacheについての知識など、実はJSP以外の部分について多くの概念を学び理解しておく必要があります。

■Webアプリケーションを構成する要素

 JSPとApacheで実現するWebアプリケーションは、大きく分けて次の4つの要素で構成されます(図1)

図1 Webアプリケーションを構成する4つの要素 図1 Webアプリケーションを構成する4つの要素
(1) JSP 機能と言語仕様
(2) Apache Webサーバとしての機能
(3) HTTP サーバとブラウザが通信をするためのルール
(4) HTML Webページを記述するための言語

 本連載では、この4つの要素のうち、(1)のJSPの機能と言語仕様に主眼を置いていますが、ほかの3つの要素なしにはWebプログラミングは成り立ちません。この4つの関係をイメージしながら、JSPのプログラムを見ていくことで、全体の見通しがよくなるでしょう。例えば処理結果を見栄えよく出力するためにはHTMLについての知識を用い、フォームからのデータの送信にPOSTやGETを使用する際にはHTTP通信のことをイメージしながら見ていくとよいでしょう。以下に、HTML、Apache、HTTPのそれぞれについて簡単にまとめます。

●HTML

 まず、ユーザーからのデータの受け渡しを行うための<FORM>タグによるHTMLの記述は、最低限理解する必要があります。HTMLに関する解説は、すでに数多くの書籍が出版されていますので、それらを参考にするとよいでしょう。また、JSPではHTMLによるプレゼンテーション部とプログラムコードによるロジック部の分離が容易に行えますので、開発者はロジック部に専念し、高度なページ表現はHTMLデザインの専門家にゆだねてしまうことが可能です。

●Apache

 本連載ではWebサーバにApacheを用いるものとしていますが、もちろんIISなどのほかのWebサーバでもJSPを動かすことは可能です。また、これらのWebサーバを使用せず、サーブレットコンテナ自身のWebサーバ機能を使用することも可能です。本連載第2回目の環境構築で説明した手法は、Apacheにアドオンして使用する方法で、Webサーバとサーブレットコンテナ間でソケット通信が行われています。この方法では、JSPからApacheに特別固有の機能を使用することはありませんが、Webサーバに備わった便利な機能を使用するにはApacheに関する知識が必要です。Apacheの機能について詳細を知りたい方はLinux Squareフォーラムの「ApacheによるWebサーバ構築」が参考になると思います。

●HTTP

 クライアントとサーバがHTTPプロトコルで通信をする以上、そのデータのやりとりにはHTTPの制限が付きます。Webプログラミングを行ううえで、HTTPについての知識を持つことは重要です。例えば、HTTPには一連のページ要求をセッションとして扱う機能が備わっていないため、クッキーやJSPで準備されたsessionオブジェクトの利用など、セッションを扱うための仕組みの実装はJSP側から提供されています。これ以降では、このHTTP通信でやりとりされるデータについて、HTTPヘッダと環境変数をJSPで扱う方法について説明します。

■HTTPヘッダの取得

 HTTPヘッダは、ブラウザとサーバが通信を行う際にデータの始めに付加する一連の情報です。この情報には、クライアントが利用しているブラウザソフトの名称やクッキー情報、ホスト名などが含まれます。

 PerlなどのCGIでヘッダ情報にアクセスするには、例えば「HTTP_USER_AGENT」という文字列で表現される変数の値を参照しますが、JSPではrequestオブジェクトのgetHeader(java.lang.String name)メソッドを用いて取得します。例えば、クライアントが使用しているブラウザの名称を取得したい場合には、

String user_agent = request.getHeader("user-agent");

とすることで、ブラウザの名称を文字列で取得できます。また、requestオブジェクトのgetHeaderNames()メソッドでは、ヘッダに含まれる変数名の一覧を取得できます。このメソッドで取得した各変数名をgetHeaderメソッドの引数にすることで、すべてのヘッダ情報を取得できます。次のプログラムは、この手法を用いてヘッダ情報に含まれる値をすべて表示する例です。

<%@ page contentType="text/html; charset=euc-jp" %>
<%
 // ヘッダ情報の変数名一覧を取得する
 java.util.Enumeration enum = request.getHeaderNames();
 
 // ヘッダ情報一覧表用のHTMLを格納する文字列
 String rows = "";
 
 // 各ヘッダ情報についての処理
 while(enum.hasMoreElements()) {
   // ヘッダ情報の変数名を取得
   String name = (String)enum.nextElement();
 
   // ヘッダ情報の値を取得
   String value = request.getHeader(name);
 
   // 一覧表用のHTMLを作成する
   rows += "<tr><td>" + name + "</td><td>" +
     value +"</td></tr>";
 }
 %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
 <head>
 <title>ヘッダ情報の取得</title>
 </head>
 <body>
 <h1>ヘッダ情報の取得</h1>
 <p>
 <table border=1>
 <tr><td>変数名</td><td>値</td></tr>
 <%= rows %>
 </table>
 </p>
 </body>
 </html>

実行結果は次のようになります。

実行結果は皆さんがご使用の環境によって異なります。変数“host”にはご使用のホスト名が表示されます 実行結果は皆さんがご使用の環境によって異なります。変数“host”にはご使用のホスト名が表示されます

最初からHTTPヘッダのクッキーにJSESSIONIDという変数が設定されています。これは、JSPがセッションの管理を行うために、ブラウザごとに割り当てるセッションのIDです。セッションを意識しなくとも、JSPではデフォルトでセッション管理を行う仕組みが準備されていることが分かります。


 例えば、このヘッダ情報の中のuser-agentの値によってユーザーが使用しているブラウザを判別することが可能です。携帯電話からのアクセスにはデータサイズの小さいページを表示するなど、ブラウザに応じて内容を切り替える処理を行うことができます。

■サーバ変数の取得

 サーバ変数は、プログラムを実行する際に必要な情報を、サーバ側であらかじめ用意したものです。PerlなどのCGIでは直接変数を参照できますが、JSPではそれぞれの変数に対応したメソッドがrequestオブジェクトに準備されており、そのメソッドを介して変数を取得します。主な変数を取得するメソッドは次の表のとおりです。

CGIで用いる変数名 変数の値を取得するメソッド 説明
REMOTE_HOST getRemoteHost() ページを要求してきたクライアントのホスト名
REMOTE_ADDR getRemoteAddr() ページを要求してきたクライアントのIPアドレス
SERVER_NAME getServerName() サーバ側のホスト名
PATH_INFO getPathInfo() 要求されたURLのパス部分
PATH_TRANSLATED getPathTranslated() 要求されたパスをサーバ側のパスに変換したもの

 サーバ変数は、さまざまな利用の方法が考えられます。例えば、リモートホストごとに利用の制限を設定したり、サーバ名をリンク文字列の中に埋め込んだりします。

これらのサーバ変数を確認するJSPプログラムがTomcatに付属のサンプルにあります。このサンプルをご自身の環境で実行して、実際に変数の値がどのようになっているか確認してみましょう。

http://server_name/examples/jsp/snp/snoop.jsp

をURL欄に入力することで実行できます。このプログラムのソースコードを見て、変数を取得するメソッドの使用方法を確認してみましょう。

server_nameはご自身の環境のサーバ名に置き換えてください。


■JSPでのWebアプリケーションの構成

 本連載の第2回目では、Context Pathの設定を行い、任意のディレクトリにJSPプログラムを配置する方法を説明しました。JSPでは、このContext Pathで設定されたディレクトリを1つのWebアプリケーションと見なして管理します。ここでのWebアプリケーションには、JSPプログラムファイルのほかに、HTMLファイル、サーブレット、オリジナルクラスなどが含まれます。いままではJSPのプログラムファイルしか扱っていませんでしたので、この構成を特に意識することはありませんでしたが、JSPではWebアプリケーションのディレクトリ構成が次のように決められています。

JSPでのWebアプリケーションのディレクトリ構成図

 $docBaseは、Context Pathの宣言で指定したdocBaseのパスです。その下に、WEB-INFというディレクトリが存在し、その中に、classeslibディレクトリ、およびweb.xmlファイルを配置します。

 classesディレクトリには、オリジナルのクラスを配置することで、JSPからそのクラスを使用することができます。lib以下に置いたクラスは、Tomcat起動時にクラスパスに追加されます。Webアプリケーションで使用するJAR形式のアーカイブを配置します。web.xmlには、サーブレットの登録や初期値の設定を行います。デフォルト値でよい場合はなくても構いません。JSPファイル、HTMLファイルは、$docBase以下の任意の場所に配置することができます。

 今後、オリジナルのクラスやアーカイブ、サーブレットを使用する場合には、このディレクトリ構成に従うようにします。

■サーバ側でのJSPの処理

 ところで、JSPは最初にリクエストがあった時点でサーバ側でコンパイルされ、コンパイルされたものが実行される、と以前に書きました。より正確には、JSPプログラムが一度Javaのコードに変換されてからコンパイルされます。それでは、次の“Hello World!”のJSPコードはどのようなJavaのコードに変換されるのでしょう?

<html>
<head><title>Hello World!</title></head>
<body>
<h1>Hello World!</h1>
<p>
<%
  String hello = "Hello World!";
  out.println(hello);
%>
</p>
</body>
</html>

 Tomcatでは、このJSPプログラムが変換されてできたJavaプログラムが、$TOMCAT_HOME/work/以下に配置されます。このプログラムを見てみると、サーバ側でどのようにJSPプログラムが処理されているのかを理解することができます。Context Pathにjspというパスを追加して、その中にhello.jspという名前でファイルを保存した場合、筆者の環境では、

localhost_8080%2Fjsp/_0002fhello_0002ejsphello_jsp_0.java

という名前で、このソースコードが存在しました。それでは、実際にこのコードの中身を見てみましょう。

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Vector;
import org.apache.jasper.runtime.*;
import java.beans.*;
import org.apache.jasper.JasperException;
 
 
public class _0002fhello_0002ejsphello_jsp_0
  extends HttpJspBase {
 
  static {
  }
  public _0002fhello_0002ejsphello_jsp_0( ) {
  }
 
  private static boolean _jspx_inited = false;
 
  public final void _jspx_init() throws JasperException {
  }
 
  public void _jspService(HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
 
    JspFactory _jspxFactory = null;
    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    String _value = null;
      try {
 
        if (_jspx_inited == false) {
          _jspx_init();
          _jspx_inited = true;
        }
      _jspxFactory = JspFactory.getDefaultFactory();
      response.setContentType("text/html;charset=8859_1");
      pageContext = _jspxFactory.getPageContext(
          this, request, response, "", true, 8192, true);
 
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
 
      // HTML // begin [file="/home/mitani/jsp/hello.jsp";from=(0,0);to=(5,0)]
      out.write("<html>\r\n<head><title>Hello World!</title></head>\r\n<body>\r\n<h1>Hello World!</h1>\r\n<p>\r\n");
      // end
      // begin [file="/home/mitani/jsp/hello.jsp";from=(5,2);to=(8,0)]
 
      String hello = "Hello World!";
      out.println(hello);
      // end
      // HTML // begin [file="/home/mitani/jsp/hello.jsp";from=(8,2);to=(11,7)]
      out.write("\r\n</p>\r\n</body>\r\n</html>");
      // end
 
    } catch (Exception ex) {
      if (out.getBufferSize() != 0)
      out.clearBuffer();
      pageContext.handlePageException(ex);
    } finally {
      out.flush();
      _jspxFactory.releasePageContext(pageContext);
    }
  }
}
注:可読性を高めるため、元のソースコードの改行位置を一部変更しています

 “Hello World!”という文字列を表示するだけのはずなのに、とても長いコードです。実際のコードを見てみると、HTML文の出力は56〜66行目で行われていることが分かります。JSPのプログラムではそのまま記述していたHTML文が、すべてout.write(String)メソッドに置き換わっていることが分かりますね。

 暗黙オブジェクトのoutは54行目で、

out = pageContext.getOut();

のように定義されています。何気なく使っていた暗黙オブジェクトが実際はどのようにして生成されているのかを知ることができます。また、そのほかにもソースコードを見ることで、次のような興味深いことが分かります。

  • JSPページによって生成されるクラスはHttpJspBaseクラスを継承している
  • HTML文の出力は_jspServiceメソッドで行われている
  • HTML文の出力、JSPページで記述した処理はtry{ }catch(Exception ex){}構文の中で行われている

HttpJspBaseクラスは、次のように、javax.servlet.http.HttpServletクラスを継承しています。つまり、JSPとは動的にサーブレットを生成するための技術である、ということが確認できます。

HttpJspBaseクラスは、javax.servlet.http.HttpServletクラスを継承している

 このほかにも、例えばディレクティブで宣言する<%@ page import="java.util.*" %>や、<%! %>タグによるメソッドの定義はソースコードのどこに反映されるのかなど、いろいろ実験してみるとよいでしょう。

 もちろん、このソースコードの存在を知らなくてもJSPを用いたプログラミングは可能ですが、これらのことを知っていると、サーバ側でJSPがどのように処理されているのかを理解する手助けになるのではないでしょうか。

 今回は、Webプログラミングの概要と、JSPでのWebアプリケーションの構成、サーバ側でのJSPの処理について解説しました。実際にWebアプリケーションを開発する場合には、JSPの知識だけでは解決できないことが多いでしょう。その際に、この概要をイメージできれば新しい学習もスムーズに進むのではないかと思います。

 次回は、JSPでのインクルード、オリジナルクラスの作成など、プログラム要素を再利用するための方法を解説します。

著者プロフィール

三谷純

タイムインターメディア



Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。