Java Servlet

1 サーブレットとは何でしょうか?

Javaスクリプトで以下のようなものを作ってみます。これはスクリプトが実行時にページにボタンを作成して表示するものです。


<html>
<head><script LANGUAGE="JavaScript">
 function setb(){
 document.write('<form method="POST">');
 document.write('<p><input type="button" value="ボタン" name="B1"></p>');
 document.write('</form>');
}
</script>
<title>新しいページ 3</title>
</head>
 
<body>
<script language="JavaScript">
setb();
</script>
</body>
</html>
 

これはJavaが動的にページを作成できる可能性を示唆します。
これをサーバ上で行うのがサーブレットです。
やっていることは似ていますが、スクリプトとは動作原理が違います。
サーブレットは動的に新しいページを作ってブラウザに返します。
アプレットとはデータのやり取りができ、大きなデータをサーバ上に持って一部をブラウザに返したり、ユーザの入力したデータをサーバ上のファイルに書き出したりできます。
また、CGIと同じように働きますが、CPUに対する負荷はサーブレットのほうが小さいとSUNは説明しています。

サーブレットのソースコードは以下のようなものです。
これはHTMLに必要なヘッダを作成した上でメッセージを表示するだけのサーブレットです。


import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class Sample1 extends HttpServlet {
 
 public void doGet(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException
 {
  ServletConfig config = getServletConfig();
  ServletOutputStream out = response.getOutputStream();
  out.println();
 
  out.println("This is my servlet!");
 }
}
 

2  開発環境のインストール JDK2.12の場合

JDKはすでインストールされていることとします。
サーブレットの開発には、Javaアプレットの開発環境(JDK)と、サーブレットの開発環境(JSDK)が両方必要です。JDKはすでインストールされていることとします。

まず、JSDK2.1をhttp://developer.java.sun.com/developer/earlyAccess/jsdk/index.html
からダウンロードします。登録(無料)が必要です。
ダウンロードしたら、解凍します。JSDK2.1というディレクトリができるので、例によって「.」をとっておきます。これでインストールは完了です。このディレクトリは都合のよい場所に移動しても大丈夫です。

(注)現在(2000−6−7)、2.1アーリーアクセスはテスト用らしくて実際にサーバで動かすことはできません。実際に使う場合はJSDK2.0か、JSDK1.2などを使ってください。しかし、次に書いてあるサーブレットランナーは、2.1にしかついていません。

3  開発環境の動作確認  サーブレットランナー

まず、サーブレットの動作確認をします。JSDKの中にはサーブレットランナーというものがあります。パソコン上のサーブレットを動作させることができます。つまり、インターネットエクスプローラやネットスケープにて自分の作ったサーブレットを見ることができます。
DOS窓をひらいでJSDK21のディレクトリに移り、RUNNER と入力しすると、
D:\jsdk21>runner
JSDK Server Version 2.1 Early Access 1

というメッセージがでます。これでランナーが動いています。

サーブレットランナーは、HTTPサーバのように働きます。ここでブラウザを立ち上げましょう。アドレスには http://127.0.0.1:localmachine:8080/ あるいは http://192.168.0.160:8080/ と入力します。

JavaTM Servlet Development Kit
Version 2.1 Early Access Release 1

というページが出ましたか?

エクスプローラがネットに接続しにいく場合があります。そこでキャンセルすると、アクセスを止めてしまう場合は接続の設定でプロキシを使うように設定を変えてください。プロキシのアドレスは、上記の場合だと 192.168.0.1 ポートは 8080 にします。

これは実際には JSDK21\httproot にあるページが表示されています。これでウィンドウズ上でサーブレットランナーが走らせてサーブレットの動作確認ができるようになりました。

上の例はHTMLファイルを表示していますが、次にサーブレットの動作を見てみます。アドレスに

http://192.168.0.1:8080/servlet/SnoopServlet

と入力します。するとサンプルの SnoopServlet というサーブレットが起動してブラウザの情報が表示されます。これはサーブレットが動的にHTMlファイルを作ったページです。

実際のディレクトリをみると、servlets となっていますが、アドレスは servlet であることに注意。

ランナーを終了するには別のDos窓をひらいで

CTRL-Cで終わります。(正しくないみたいですが)

4  とりあえずサーブレットを作ってみる

以下のコードは一応動くサーブレットのコードです。日本語がとおるようになっています。
見てみると、なーんだ、という感じのソースですね。
HTMLのコードをそのままプリントしてるだけじゃん。
と思った方、そのとおりです。
静的なページならこのようにして表示することができます。

簡単にできそうでしょ?

どのように動くか、まずはテストしてみましょう。


import java.io.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class Sample1 extends HttpServlet{
 int count=0;
 
 protected void doGet(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException{
  count++;
  Date d = new Date();
 
  PrintWriter pw =
   new PrintWriter(new BufferedWriter(new
   OutputStreamWriter(res.getOutputStream(),"JIS")));
  res.setContentType("text/html; charset=iso-2022-jp");
 
  /*HTMLファイルの中身を記述する*/
  pw.println("<html>");
  pw.println("<head><title>ServletTest</title></head>");
  pw.println("<body bgcolor=\"#eeeeff\" text=\"#808080\">");
  pw.println("<font size=\"5\" color=\"#206060\">");
  pw.println("<b><i>Servlet Test</i></b></font><br>");
  pw.println("<font size=\"2\"><i>サーブレットが動いた、ばんざーい<br>");
 
  pw.println("ついでにボタンをつけてみます");
 
  pw.println("<form method=\"POST\" action=\"--WEBBOT-SELF--\">");
  pw.println("<p><input type=\"button\" value=\"ボタン\" name=\"B1\"></p>");
 
  pw.flush();
  pw.close();
  }
 
 public String getServletInfo(){
 return "This is servlet test program";
 }
}
 

5  サーブレット の コンパイル

まず、Dos窓を開きます。そしてサーブレットのソースのあるディレクトリに移動して、以下のようにパスをセットします。コンパイラから servlet.jar が見えればいいのですね。ドライブがDでは無い人は間違えないで自分のJSDKのあるパスを指定してください。
set classpath = d:\jsdk21\servlet.jar

VC++的にいうと、サーブレットクラスのヘッダファイルのディレクトリにパスを通すということです。

コンパイルはアプレットと同じで

javac Sample1.java

でOKです。例によって大文字と小文字を間違えないように。
コンパイルに成功すると、 Sample1.class というファイルができます。

注 Jarファイルというのはクラスがたくさん入ったパッケージのことです。servlet.jar はもちろんサーブレットに関係したクラスが入っています。Jarはもちろん、お茶の入れ物のことです。

6  動作確認

コンパイルしてエラーが無くなったら、次は動作確認です。
クラスファイル( Sample1.class ) を d:\jsdk21\webapp\servlets に移動させます。これはDドライブにJSDKがある場合です。
移動させたら、次にRUNNERを起動します。クラスは更新するたびにRUNNERも起動しなおさないと変更がブラウザに反映されません。これはRUNNERがサーブレットをメモリに読み込んでメモリ上で実行しているためだと思われます。

さてコピーが終わったらブラウザを立ち上げてアドレスを入力します。アドレスは、あなたのコンピュータのローカルアドレスです。LANを組んでいる場合はたとえば  192.168.0.15 などになります。サーブレットにアクセスするためのアドレスは、

http://192.168.0.15:8080/servlet/Sample1

となります。

わからない場合、あるいはLANを組んでいない場合は

http://127.0.0.1:8080/servlet/Sample1 か

http://localhost:8080/servlet/Sample1

と入力してみてください。だめならローカルアドレスを設定します。ローカルアドレスの設定の仕方は、各自しらべてください。


以下のようなページが表示されたらナイスですね。



7  サーバ上のファイルに対する読み書き  1

アプレットのよいところは、サーバ上でファイルの読み書きができることです。ですから、データをファイルとして別に持ったり、ユーザの入力をサーバ上に保存したりできます。アプレットはこれができなかったので、CGIを利用していました。サーブレットは比較的簡単にCGIと同じことができます。
以下の例は、ファイルに書き込むサーブレットの例です。「ひよこサーブレット」にあるサンプルに手を加えてます。


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class SaveData extends HttpServlet{
 long count;
 String txt_data;
 
 protected void doGet(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException{
 
  count = 12349;
 
  //パラメータを取得し、カウント・データを任意のパスに設定することが出来る。
  //また、デフォルト設定ではリクエストHTML位置から見て[data/count.dat]の相対パスの
  //ファイルをカウント・データ・ファイルとしている。
  //注意:データ・ファイル(count.dat)は自動で生成されないので、別途準備しておくこと。
  //尚、初期値の数値を記述しておくこと。例えば"0"を。
 
  String file_name =req.getParameter("nam");
  if(file_name == null){file_name="/count.dat";}
  String path_name = req.getPathTranslated(); -----セーブするディレクトリの取得
  String file_path =path_name.substring(0,path_name.lastIndexOf('/')) + file_name;
 
  //オキマリの出力ストリーム取得
  PrintWriter pw =
   new PrintWriter(new BufferedWriter(new
   OutputStreamWriter(res.getOutputStream(),"JIS")));
  res.setContentType("text/html; charset=iso-2022-jp");
 
  //カウント・データの読み込み
  //上記より得られた[file_path]に書かれた数値を読みに行く
  try{
   BufferedReader file_read = new BufferedReader(new
   FileReader(file_path));
   txt_data = file_read.readLine();
   file_read.close();
  }catch(IOException e){
   //エラーがあればブラウザに出力する
   pw.println("&lt;could not read count-data&gt;");
  }
 
  // 取得したファイルパスを確認する
  pw.println(file_path);
 
  //カウント・データ(String)をlong型に変換し、更にカウント・アップする。
  count = Long.valueOf(txt_data).longValue();
  count++;
 
  //カウント・アップしたデータをファイルに書き込み、保存する
  //先ほどの[file_path]にデータを上書きする
  try{
   PrintWriter file_write = new PrintWriter(new
   FileOutputStream(file_path));
   file_write.println(count);
   file_write.close();
  } catch(IOException e){
   //エラーがあればブラウザに出力する
   pw.println("&lt;could not write count-data&gt;");
  }
 
  pw.println("<font size=\"2\"><i>サーブレットが動いた、ばんざーい<br>");
 
  //ブラウザにカウント値を出力しておしまい
  pw.println(count);
  pw.flush();
  pw.close();
  }
 
 public String getServletInfo(){
 return "This is simple text counter program";
 }
}
 

実行してみるとわかりますが、データのディレクトリを取得すると、それが
D:\jsdk21\httproot/servlet/count.dat となります。
その場所に、count.dat が無い場合は初回にエラーが出ます。

これはファイル内の数字を表示してあと、カウントアップして再びファイルに書き込んでいます。更新すると数字がひとつづつ増えていきます。

7  サーバ上のファイルに対する読み書き  2 データを蓄積する


6の例では、ファイルを書き換えているので、常に数字データがひとつファイルにあります。ユーザデータを蓄積する場合はファイルに追加してデータを書き込んでいかなければいけません。そのやり方は以下のとおり。
テスト中

8 サーブレットにパラメータを渡す

サーブレットが仕事をするにしてもHTMLからパラメータを渡す必要があります。
パラメータを渡す方法には、二つあります。ひとつは doGet と doPost という、二つの方法があります。doGetはサーブレットのリクエストにくっつけて渡します。具体的にはブラウザのアドレスにパラメータをくっつけて
http://192.168.0.1:8080/servlet/pasTest?para1=1234&para1=9987&para2=鈴木京香

というようにリクエストします。  

緑部分がサーブレット名で、赤部分がパラメータです。?と&の位置に注目してください。para1とpara2にパラメータを渡しています。para1 が2個あるのにも注目。同じ名前のパラメータがあってもOKです。

受け取るほうのサーブレットは、この場合は doGet のなかにそれぞれ

String[ ] strPara1 = req.getParameterValues("para1");
String[ ] strPara2 = req.getParameterValues("para2");

とすることでstrPara1,2 にそれぞれのパラメータを受け取ることができます。これはCGIにパラメータを渡す方法と同じです。サーチエンジンと同じで、パラメータの中身はよく目にしますね。

Stringが配列になっているのは、para1,para2などのパラメータが複数ある場合のためで、出現した順番に配列に格納されます。

受け渡しの方法でもわかりますが、ブラウザの画面はかならず更新されます。新しいページが表示されるわけですね。

次の方法がdoPostという方法です。これはパラメータの数が多い場合に使うようです。

サーブレットのdoPostが呼び出されるのは、HTMLファイルのFORMで、送信した場合です。
パスワード 送信  などのボタンがある場合ですね。そのときに入力フィールドに入っているパラメータがdoGetと同じようにdoPostに渡されます。パラメータの受け取り方法も、返すHTMLの書き方も基本的には同じです。

渡されるパラメータは、名前=パラメータ の書式になります。

たとえば  <p>稼働時間     <input type="text" name="T2" size="20" value="おめでとう"></p> というフィールドを送信すると受け取るパラメータは

T2=おめでとう になります。

HTMLファイルと呼び出されるサーブレットの位置関係は調べ中

9 サーバ上にある、HTMLファイルを読み込んでそれを出力するサーブレット


// HTMLファイルを読み込みこんでそれを出力する
 
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
 
public class Text0 extends HttpServlet{
 long count;
 StringBuffer txt_data,txt_data1;
 
 protected void doGet(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
  {
 
  count = 12349; // 初期化テスト
 
  // 可変長配列の初期化
  Vector cashData = new Vector();
 
  //読み込むファイルのパスを取得する
  String file_name =req.getParameter("nam"); // パラメータが無い場合は
  if(file_name == null){file_name="/count.htm";} // デフォルトのファイル名を入れる
  // デフォルトのディレクトリは、jsdk21/httproot/servlet/
  String path_name = req.getPathTranslated(); // パス名を取得する
  // パス名とファイル名を結合する
  String file_path =path_name.substring(0,path_name.lastIndexOf('/')) + file_name;
 
  //オキマリの出力ストリーム取得日本語対応
  PrintWriter pw =
   new PrintWriter(new BufferedWriter(new
   OutputStreamWriter(res.getOutputStream(),"JIS")));
  res.setContentType("text/html; charset=iso-2022-jp");
 
  //カウント・データの読み込み
  //上記より得られた[file_path]に書かれた数値を読みに行く
  try{
   BufferedReader file_read = new BufferedReader(new FileReader(file_path)); //
 
  ///////////////////////////
  int i;
  // HTMLファイルを全部読み込む
  while(true){
   String txt_datax = file_read.readLine(); // ファイルから1行読み出す
   if(txt_datax == null) break;
   cashData.addElement(txt_datax); // Vector に呼んだ行を追加する
  }
  file_read.close(); // ファイルを閉じる
 
  ////////////////////////////
 
  }catch(IOException e){
   //エラーがあればブラウザに出力する
   pw.println("&lt;could not read count-data&gt;");
  }
 
  pw.println(file_path); // ファイルパスを出力する(デバッグ用)
 
  pw.println("<p>以下はHTMLファイルからの転送です</p><br>");
 
  int i;
  // 読み込んだすべての要素をブラウザに渡す
  for(i=0; i<cashData.size();i++)
  {
   pw.println(cashData.elementAt(i)); // 読み込んだHTMLを規定行数分出力する
  }
 
  pw.flush(); // PWの後始末
  pw.close();
 
  }
 
 public String getServletInfo(){
 return "This is simple text counter program";
 }
}
 

10 Package

クラスファイルが多くなってくると、servletディレクトリがいっぱいになるので別にディレクトリを作ってそこに分けて入れるようにします。
ソースファイルの先頭にたとえば
package jp.ne;

と書きます。こうすると、このファイルは、servlet/jp/ne というディレクトリに置くことができます。
ウィンドウズのRUNNER環境では servlet\jp\ne というディレクトリを作ってそこにクラスファイルを入れます。そのサーブレットにアクセスするアドレスは

http://192.168.0.1:8080/servlet/jp.ne.TextCount のようになります。

パッケージのアドレスはピリオドで区切ります。

プログラムが自分のあるべき位置をプログラム中で定義するというのはウィンドウズではなじみのない方法です。これがUNIX流ってやつでしょうか。私はよく知りませんが。

*DXSサンプルコード