ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server Web アプリケーション、サーブレット、JSP の開発
11g リリース 1 (10.3.1)
B55521-01
  目次
目次

戻る
戻る
 
次へ
次へ
 

9 サーブレット プログラミング タスク

以下の節では、WebLogic Server 環境で HTTP サーブレットを記述する方法について説明します。

サーブレットの初期化

通常、WebLogic Server によってサーブレットが初期化されるのは、そのサーブレットに対して最初の要求が出されたときです。その後、そのサーブレットが変更されると、既存のバージョンのサーブレットに対して destroy() メソッドが呼び出されます。変更後のサーブレットに要求が送信されると、変更後のサーブレットの init() メソッドが実行されます。詳細については、「サーブレットのベスト プラクティス」を参照してください。

サーブレットが初期化されるとき、WebLogic Server はサーブレットの init() メソッドを実行します。サーブレットは一度初期化されると、WebLogic Server を再起動するまで、またはサーブレット コードが変更されるまで、再び初期化されることはありません。init() メソッドをオーバーライドすると、データベース接続の確立など、サーブレットの初期化時に特定のタスクを実行させることができます (「init() メソッドのオーバーライド」を参照)。

WebLogic Server 起動時のサーブレットの初期化

サーブレットに最初の要求が送信された時に WebLogic Server がサーブレットを初期化するのではなく、サーバの起動時に初期化するように、最初に WebLogic Server をコンフィグレーションできます。そのためには、J2EE 標準の Web アプリケーション デプロイメント記述子 web.xmlload-on-startup 要素内にサーブレット クラスを指定します。Web アプリケーション内でのリソースの初期化の順序は、次のとおりです。

  1. ServletContextListeners - この Web アプリケーションについて登録された ServletContextListeners に対する contextCreated() コールバック。

  2. ServletFilters init() メソッド。

  3. Servlet init() メソッド (web.xml 内では load-on-startup としてマーク)。

初期化中に HTTP サーブレットにパラメータを渡すには、サーブレットを含む Web アプリケーションでパラメータを定義します。このパラメータを使用すると、サーブレットが初期化されるたびに書き換えることなく値を渡せます。

たとえば、J2EE 標準の Web アプリケーション デプロイメント記述子 web.xml の以下のようなエントリの場合、次の 2 つの初期化パラメータが定義されます。1 つは Welcome の値を持つ greeting、もう 1 つは WebLogic Developer の値を持つ person です。

<servlet>
  ...
 <init-param>
    <description>The salutation</description>
    <param-name>greeting</param-name>
    <param-value>Welcome</param-value>
  </init-param>
 <init-param>
    <description>name</description>
    <param-name>person</param-name>
    <param-value>WebLogic Developer</param-value>
  </init-param>
</servlet>

初期化パラメータを取得するには、親 javax.servlet.GenericServlet クラスから getInitParameter(String name) メソッドを呼び出します。パラメータ名を渡されると、このメソッドはパラメータの値を String として返します。

init() メソッドのオーバーライド

init() メソッドをオーバーライドすることで、初期化時にサーブレットにタスクを実行させることができます。次のコードでは、J2EE 標準の Web アプリケーション デプロイメント記述子 web.xml の中で挨拶文と名前を定義する <init-param> タグを読み込んでいます。

String defaultGreeting;
String defaultName;
public void init(ServletConfig config) 
    throws ServletException {
  if ((defaultGreeting = getInitParameter("greeting")) == null)
    defaultGreeting = "Hello";
  if ((defaultName = getInitParameter("person")) == null)
    defaultName = "World";
}

各パラメータの値は、クラス インスタンス変数 defaultGreeting および defaultName に格納されます。最初にパラメータが null 値を持つかどうかをテストして、null 値が返されれば、適切なデフォルト値を提供します。

これで、service() メソッドを使用して、応答の中にこれらの変数を含めることができます。次に例を示します。

out.print("<body><h1>");
out.println(defaultGreeting + " " + defaultName + "!");
out.println("</h1></body></html>");

サーブレットの init() メソッドは、WebLogic Server がサーブレットをロードするときに必要となるすべての初期化を行います。デフォルトの init() メソッドは WebLogic Server が必要とする初期作業をすべて行うので、特別な初期化を行う場合を除いて、このメソッドをオーバーライドする必要はありません。init() をオーバーライドする場合は、デフォルトの初期化アクションが先に実行されるように、まず super.init() を呼び出します。

HTTP 応答の提供

この節では、HTTP サーブレットでクライアントへの応答を提供する方法について説明します。応答はすべて、サーブレットの service() メソッドにパラメータとして渡される HttpServletResponse オブジェクトを使用して渡さなければなりません。

  1. HttpServletResponse をコンフィグレーションします。

    HttpServletResponse オブジェクトを使用して、HTTP ヘッダ情報に変換される複数のサーブレット プロパティを設定できます。

    • 少なくとも、ページのコンテンツを書き込む出力ストリームを取得する前に、setContentType() メソッドを使用してコンテンツ タイプを設定する。HTML ページの場合、コンテンツ タイプは text/html に設定します。次に例を示します。

      res.setContentType("text/html");
      
    • (省略可能) setContentType() メソッドを使用して文字エンコードを設定することもできる。次に例を示します。

      res.setContentType("text/html;ISO-88859-4");
      
    • setHeader() メソッドを使用して、ヘッダ属性を設定する。動的な応答の場合は、Pragma 属性を no-cache に設定するとよいでしょう。こうするとブラウザは常にページを再ロードし、確実に最新データを表示します。次に例を示します。

      res.setHeader("Pragma", "no-cache");
      
  2. HTML ページを作成します。

    サーブレットがクライアントに送り返す応答は、通常の HTTP コンテンツ (基本的には HTML 形式) のように見える必要があります。サーブレットは、service() メソッドの応答パラメータを使用して取得した出力ストリームを通じて、HTTP 応答を送り返します。HTTP 応答を送信するには、次の手順に従います。

    1. HttpServletResponse オブジェクトおよび次の例のいずれかのメソッドを使用して、出力ストリームを取得します。

      • PrintWriter out = res.getWriter();

      • ServletOutputStream out = res.getOutputStream();

      同じサーブレット内 (または、サーブレットの中にインクルードされた別のサーブレット内) で、PrintWriterServletOutputStream を両方とも使用できます。どちらの出力も、同じバッファに書き込まれます。

    2. print() メソッドを使用して、応答の内容を出力ストリームに書き出します。これらの文では、HTML タグを使うことができます。次に例を示します。

      out.print("<html><head><title>My Servlet</title>");
      out.print("</head><body><h1>");
      out.print("Welcome");
      out.print("</h1></body></html>");
      

      ユーザが以前に入力したデータを出力する場合、HTML 特殊文字が入力されていればすべて削除することをお勧めします。これらの文字を削除しない場合、クロスサイト スクリプティングによって Web サイトが損なわれるおそれがあります。詳細については、「サーブレットでのクライアント入力のセキュリティ」を参照してください。

      出力ストリームは close() メソッドでクローズしないでください。また、ストリームのコンテンツをフラッシュすることも避けてください。出力ストリームをクローズしたりフラッシュしたりしないことによって、次の手順で説明する永続的 HTTP 接続の利点を活かすことができます。

  3. 応答を最適化します。

    デフォルトでは、WebLogic Server は、可能な限り HTTP の永続的接続を使用しようとします。永続的接続は、クライアントとサーバ間の一連の通信のために、同一の HTTP TCP/IP 接続を再利用しようとします。要求ごとに新しい接続をオープンする必要がないので、アプリケーションの性能を高めることができます。永続的接続は、HTML ページにインライン画像が多く含まれる場合に便利です。接続を再利用しないと、画像が要求されるごとに新しい TCP/IP 接続が必要になるためです。

    WebLogic Server Administration Console を使用すると、WebLogic Server が HTTP 接続をオープンに保つ時間をコンフィグレーションできます。

    永続的接続を確立するために、WebLogic Server は HTTP 応答の長さを知る必要があるので、HTTP 応答ヘッダに Content-Length プロパティを自動的に追加します。コンテンツ長を確認するために、WebLogic Server では応答をバッファリングする必要があります。ただし、サーブレットが ServletOutputStream を明示的にフラッシュすると、WebLogic Server では応答の長さを判別できないため、永続的接続を使用できません。このため、サーブレットで HTTP 応答を明示的にフラッシュすることは避けます。

    場合によっては、ページ完成前にクライアントに情報を表示するために応答を早くフラッシュした方がよいこともあります。たとえば、時間のかかるページのコンテンツを計算している途中で、バナー広告を表示する場合などです。逆に、サーブレット エンジンが使用するバッファ サイズを増やして、フラッシュする前に、もっと長い応答を入れておきたいという場合もあります。javax.servlet.ServletResponse インタフェースの関連メソッドを使用して、応答バッファのサイズを操作することができます。詳細については、http://java.sun.com/products/servlet/download.html#specs の Servlet 2.4 仕様を参照してください。

    WebLogic Server の応答バッファのデフォルト値は 12K で、バッファ サイズは、CHUNK_SIZE (CHUNK_SIZE = 4088 バイト) に基づいて内部で計算されます。たとえば、ユーザが 5KB に設定すると、バッファ サイズは、それを切り上げた最も近い CHUNK_SIZE の倍数 (この場合は 2) である 8176 バイトに設定されます。

クライアント入力の取得

HTTP サーブレット API は、Web ページからユーザ入力を取得するためのインタフェースを提供しています。

Web ブラウザからの HTTP リクエストには、クライアント、ブラウザ、クッキー、およびユーザのクエリ パラメータに関する情報といった、URL 以外の情報も含めることができます。ブラウザからのユーザ入力を渡すには、クエリ パラメータを使用します。GET メソッドは URL アドレスにパラメータを付加し、POST メソッドはそれらを HTTP リクエスト本文の中に含めます。

HTTP サーブレットでは、これらの細部を扱う必要はありません。要求の中の情報は、送った方法に関係なく、HttpServletRequest オブジェクトを通じて取得され、request.getParameter() メソッドを使用してアクセスできるようになります。

クエリ パラメータをクライアントから送る方法については、以下を参照してください。

クエリ パラメータは常に名前 = 値の組で送られ、HttpServletRequest オブジェクトを通じてアクセスされます。クエリのすべてのパラメータ名の Enumeration を取得し、そのパラメータ名を使用して各パラメータの値を取得できます。1 つのパラメータの値は通常 1 つだけですが、値の配列を持つこともできます。パラメータの値は常に String として読み取られるので、より適切な型にキャストすることが必要な場合もあります。

service() メソッドからの次のサンプルでは、フォームから得たクエリ パラメータ名とその値を調べます。requestHttpServletRequest オブジェクトであることに注意してください。

Enumeration params = request.getParameterNames();
String paramName = null;
String[] paramValues = null;

while (params.hasMoreElements()) {
  paramName = (String) params.nextElement();
  paramValues = request.getParameterValues(paramName);
  System.out.println("\nParameter name is " + paramName);
  for (int i = 0; i < paramValues.length; i++) {
    System.out.println(", value " + i + " is " +
                         paramValues[i].toString());
  }
}

注意 :

ユーザが入力したデータを出力する場合、HTML 特殊文字が入力されていればすべて削除することをお勧めします。これらの文字を削除しない場合、クロスサイト スクリプティングによって Web サイトが損なわれるおそれがあります。詳細については、「サーブレットでのクライアント入力のセキュリティ」を参照してください。

HTTP リクエストを使用するメソッド

この節では、要求オブジェクトからデータを取得できる javax.servlet.HttpServletRequest インタフェースのメソッドを定義します。次の制限事項に注意してください。

  • この節の getParameter() メソッドを使用して要求パラメータを読み込んだ後に、getInputStream() メソッドで要求を読もうとすることはできない。

  • getInputStream() で要求を読み込んだ後に、getParameter() メソッドの 1 つを使って要求パラメータを読み込もうとすることもできない。

上のいずれかの手順を実行しようとすると、illegalStateException が送出されます。

javax.servlet.HttpServeletRequest の次のメソッドを使用して、要求オブジェクトからデータを取得できます。

  • HttpServletRequest.getMethod() - GETPOST などの要求メソッドを決定できます。

  • HttpServletRequest.getQueryString() - クエリ文字列にアクセスできます (クエリ文字列は、要求された URL で ? の後に続く部分です)。

  • HttpServletRequest.getParameter() - パラメータの値を返します。

  • HttpServletRequest.getParameterNames() - パラメータ名の配列を返します。

  • HttpServletRequest.getParameterValues() - パラメータの値の配列を返します。

  • HttpServletRequest.getInputStream() - 要求本体をバイナリ データとして読み込みます。要求パラメータを getParameter()getParameterNames()、または getParameterValues() で読み込んだ後にこのメソッドを呼び出すと、illegalStateException が送出されます。

例 : クエリ パラメータによる入力の取得

この例では、より個人的な挨拶文を表示するために、クエリ パラメータとしてユーザ名を受け付けるように HelloWorld2.java サーブレットのサンプルを修正しています。service() メソッドを次に示します。

コード リスト 9-1 service() メソッドによる入力の取得

public void service(HttpServletRequest req,
         HttpServletResponse res)
     throws IOException
{
  String name, paramName[];
  if ((paramName = req.getParameterValues("name")) 
      != null) {
    name = paramName[0];
  }
  else {
    name = defaultName;
  }

  // まず、コンテンツ タイプを設定する
  res.setContentType("text/html");
  // PrintWriter を出力ストリームとして取得する
  PrintWriter out = res.getWriter();

  out.print("<html><head><title>" + 
              "Hello World!" + </title></head>");
  out.print("<body><h1>");
  out.print(defaultGreeting + " " + name + "!");
  out.print("</h1></body></html>");
}

getParameterValues() メソッドは、HTTP クエリ パラメータから name パラメータの値を取得します。これらの値は、String タイプの配列で取得します。このパラメータに 1 つの値が返され、name 配列の第 1 要素に割り当てられます。クエリ データにこのパラメータがなければ、戻り値は null になります。この場合は、init() メソッドによって <init-param> から読み込んだデフォルトの名前に name を割り当てます。

パラメータが HTTP リクエストに含まれていると仮定してサーブレット コードを記述しないでください。getParameter() メソッドは非推奨となったため、配列の添え字にその末尾までタグ付けすることによって、getParameterValues() メソッドを略記しようとする場合があります。しかし、このメソッドは指定したパラメータが有効でないと null を返すことがあり、NullPointerException が発生します。

たとえば、次のコードでは NullPointerException が発生します。

String myStr = req.getParameterValues("paramName")[0];

代わりに、次のコードを使用します。

if ((String myStr[] = 
          req.getParameterValues("paramName"))!=null) {
  // これで myStr[0] を使うことができる
}
else {
  // paramName がクエリ パラメータの中になかった!
}

サーブレットでのクライアント入力のセキュリティ

ユーザが入力したデータを取得して返す機能があると、クロスサイト スクリプティングと呼ばれるセキュリティの脆弱性がもたらされます。これは、ユーザのセキュリティ認可を盗用するために利用される可能性があります。クロスサイト スクリプティングの詳細については、http://www.cert.org/tech_tips/malicious_code_mitigation.html の「Understanding Malicious Content Mitigation for Web Developers」(CERT のセキュリティ勧告) を参照してください。

セキュリティの脆弱性をなくすには、ユーザが入力したデータを返す前に、そのデータをスキャンして、表 9-1 に示す HTML 特殊文字があるかどうかを調べます。特殊文字が見つかった場合、HTML のエンティティ参照または文字参照と置き換えます。文字を置換することで、ブラウザでユーザ入力によるデータが HTML として実行されることを回避します。

表 9-1 置き換えの必要がある HTML の特殊文字

置き換える必要のある特殊文字 置き換え後のエンティティ/文字参照

<

&lt;

>

&gt;

(

&40;

)

&41;

#

&35;

&

&38;


WebLogic Server ユーティリティ メソッドを使用する

WebLogic Server には、ユーザが入力したデータ内の特殊文字を置き換える weblogic.servlet.security.Utils.encodeXSS() メソッドが用意されています。このメソッドを使用するには、ユーザ入力データを入力として提供します。コード リスト 9-1 でユーザが指定したデータを保護するには、次の行

out.print(defaultGreeting + " " + name + "!"); 

を、以下の行と置き換えます。

out.print(defaultGreeting + " " + 
weblogic.security.servlet.encodeXSS(name) + "!"); 

アプリケーション全体を保護するため、ユーザが入力したデータは、返す際に毎回 encodeXSS() メソッドを使用する必要があります。コード リスト 9-1 での上記の例は、encodeXSS() メソッドを使用すべき場所の 1 つですが、表 9-1 ではこのメソッドを使用すべきかどうかを検討すべきその他の場所を示します。

表 9-2 ユーザ入力データを返すコード

ページのタイプ ユーザ入力データ

エラー ページ

誤りのある入力文字列、無効な URL やユーザ名

username はアクセスを許可されていません。」と表示するエラー ページ

ステータス ページ

ユーザ名、以前のページでの入力の要約

以前のページでの入力を確認するようユーザに求める要約ページ

データベース表示

データベースから提示されたデータ

以前にユーザによって入力されたデータベース エントリのリストを表示するページ


サーブレットでのクッキーの使い方

クッキーは情報の一片です。サーバはこの情報をユーザのディスク上へローカルに保存するよう、クライアント ブラウザに要求します。ブラウザは、同じサーバにアクセスするたび、HTTP リクエストと共にそのサーバに関連したクッキーをすべて送ります。クッキーは、クライアントからサーバに戻されるので、そのクライアントを識別するために便利なものです。

各クッキーは名前と値を保持しています。通常、クッキーをサポートしているブラウザでは、各サーバのドメインに、1 つあたり最大 4K のクッキーを 20 まで格納することができます。

HTTP サーブレットでのクッキーの設定

ブラウザ上にクッキーを設定するには、クッキーを作成し、値を与え、サーブレットのサービス メソッドの 2 番目のパラメータである HttpServletResponse オブジェクトに追加します。次に例を示します。

Cookie myCookie = new Cookie("ChocolateChip", "100");
myCookie.setMaxAge(Integer.MAX_VALUE);
response.addCookie(myCookie);

上記の例では、値が 100ChocolateChip と呼ばれるクッキーを、応答の送信時にブラウザ クライアントに追加します。クッキーの有効期限は指定できる最大値に設定されているため、クッキーは永久に有効です。クッキーは文字列型の値のみを受け入れるので、クッキーに格納するための必要な型との間でキャストします。EJB の場合、一般的にはクッキーの値に対する EJB インスタンスのホーム ハンドルを使用し、EJB にユーザの詳細情報を格納して、後で参照できるようにします。

HTTP サーブレットでのクッキーの取得

service() メソッドへの引数としてサーブレットに渡される HttpServletRequest から、クッキー オブジェクトを取得することができます。クッキーそのものは、javax.servlet.http.Cookie オブジェクトとして示されます。

サーブレット コードでは、getCookies() メソッドを呼び出すことにより、ブラウザから送られたすべてのクッキーを取得することができます。次に例を示します。

Cookie[] cookies = request.getCookies();

このメソッドは、ブラウザから送られたすべてのクッキーの配列を返すか、またはブラウザから送られたクッキーがない場合、null を返します。サーブレットは、その配列を処理して正しい名前のクッキーを探す必要があります。クッキーの名前は、Cookie.getName() メソッドを使って取得することができます。同一の名前で、パス属性の異なるクッキーが複数ある可能性があります。サーブレットが、同一の名前でパス属性の異なる複数のクッキーを設定した場合、Cookie.getPath() メソッドを使ってこれらを比較することも必要です。以下のコードは、ブラウザから送られたクッキーの詳細にアクセスする方法を示しています。このサーバに送られたクッキーはすべてユニークな名前を持ち、ブラウザ クライアントで以前に設定したと考えられる ChocolateChip というクッキーを探しているという前提です。

Cookie[] cookies = request.getCookies();
boolean cookieFound = false;

for(int i=0; i < cookies.length; i++) {
  thisCookie = cookies[i];
  if (thisCookie.getName().equals("ChocolateChip")) {
    cookieFound = true;
    break;
  }
}

if (cookieFound) {
  // クッキーが見つかったので、その値を取得する
  int cookieOrder = String.parseInt(thisCookie.getValue());
}

HTTP と HTTPS の両方で送信されるクッキーの使い方

HTTP リクエストと HTTPS リクエストは異なるポートに送られるので、ブラウザによっては、HTTP リクエストに入れて送られてきたクッキーを、その後続の HTTPS リクエストに包含しない (あるいはその逆) ことがあります。このような場合には、サーブレット要求が HTTP と HTTPS の間で切り替わると、新しいセッションが作成されることになります。セッション内で要求が行われるたびに、特定のドメインによって設定されるすべてのクッキーがサーバに送られるようにするには、cookie-domain 要素をドメイン名に設定します。cookie-domain 要素は、WebLogic 固有のデプロイメント記述子 weblogic.xmlsession-descriptor 要素の下位要素です。次に例を示します。

<session-descriptor>
  <cookie-domain>mydomain.com</cookie-domain>
</session-descriptor>

cookie-domain 要素は、mydomain.com によって指定されたドメイン内のホストに、すべての要求について適切なクッキーを入れるようブラウザに指示します。このプロパティやセッション クッキーのコンフィグレーションの詳細については、「セッション管理の設定」を参照してください。

アプリケーションのセキュリティとクッキー

クッキーの使用は、マシン上での自動的なアカウント アクセスを可能にして便利ですが、セキュリティの観点からは望ましくない場合があります。クッキーを使用するアプリケーション設計時には、以下のガイドラインに従ってください。

  • クッキーが常にユーザに対して正確であると考えないようにする。マシンが共有されている場合や、同一のユーザが異なるアカウントにアクセスしようとする場合もあります。

  • ユーザが、サーバ上にクッキーを残すかどうか選択できるようにする。共有マシンでは、ユーザがそのアカウントに対する自動的なログインをそのままにしておくことを望まない場合があります。ユーザがクッキーについて理解していると仮定せずに、以下のような質問をします。

    「このコンピュータから自動ログインしますか?」
    
  • 注意の必要なデータを取得するためにログインするユーザに対しては、常にパスワードを要求する。ユーザがそれ以外の方法を要求しない限り、この選択結果とパスワードはユーザのセッション データに格納できます。セッションのクッキーは、ユーザがブラウザを終了したときに有効期限が切れるようにコンフィグレーションします。

応答のキャッシュ

キャッシュ フィルタは、次の例外を除いて、キャッシュ タグと同様に動作します。

キャッシュ フィルタには、別のページに含まれていなかったページのためのキャッシュ タグにはないデフォルト動作があります。キャッシュ フィルタは、応答ヘッダ Content-Type と Last-Modified を自動的にキャッシュします。キャッシュ フィルタは、キャッシュ内に存在しているページの要求を受け取ると、要求の If-Modified-Since ヘッダと Last-Modified 応答ヘッダを比較して、実際にコンテンツを提供するか、302 SC_NOT_MODIFED ステータスと空のコンテンツを送信するかを決定します。

次の例は、J2EE 標準デプロイメント記述子 web.xmlfilter 要素を使用した、Web アプリケーション内のすべての HTML ページをキャッシュするキャッシュ フィルタの登録方法を示しています。

<filter>
  <filter-name>HTML</filter-name>
  <filter-class>weblogic.cache.filter.CacheFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>HTML</filter-name>
  <url-pattern>*.html</url-pattern>
</filter-mapping>

このキャッシュ システムは、ソフト リファレンスを使用してキャッシュを格納します。そのため、ガベージ コレクタは、キャッシュが作成または最終アクセスされてから経過した時間に基づいて、キャッシュを再要求するかどうか決定します。そして、OutOfMemoryError が発生しないように、ソフト リファレンスをクリアします。

初期化パラメータ

Web ページが更新されたときに確実に新しいコピーをキャッシュに取り込むには、フィルタにタイムアウトを追加します。キャッシュ フィルタには、キャッシュ タグと同じような初期化パラメータが多数用意されています。

初期化パラメータは次のとおりです。

  • Name - キャッシュの名前。*. 拡張子の URL パターンとの互換性を考慮して、要求 URI がデフォルト名になります。

  • Timeout - キャッシュの最終更新時から、次にキャッシュ内のコンテンツを更新するまでの時間。デフォルトの単位は「秒」ですが、ms (ミリ秒)、s (秒)、m (分)、h (時)、d (日) のいずれかの単位を指定することもできます。

  • Scope - キャッシュのスコープには、requestsessionapplicationcluster のいずれかを指定できる。request スコープを使用すると、ページ内でループ構造がある場合に便利なこともありますが、それ以外ではあまりメリットはありません。デフォルトでは、スコープは application に設定されています。cluster スコープを使用するには、ClusterListener を設定する必要があります。

  • Key - このパラメータは、キャッシュが、name だけでなく、スコープ内のさまざまなエントリの値で指定されていることを示す。CacheTag のキーと同じ要領で指定します (ただし、page スコープを使用することはできません)。

  • Vars - キャッシュしたいページに自動的に計算させる変数。一般に、入力パラメータに基づいてデータベースから情報を引き出すサーブレットで使用されます。

  • Size - キャッシュされるユニークなキー値の最大数。デフォルトでは、無制限になっています。

    次の例は、フィルタ コード内の初期化パラメータの指定場所を示します。

    <filter>
      <filter-name>HTML</filter-name>
      <filter-class>weblogic.cache.filter.CacheFilter</filter-class>
      <init-param>
    
  • Max-cache-size - キャッシュに追加される要素の最大サイズ。デフォルトは 64K です。

HTTP サーブレットからの WebLogic サービスの使い方

HTTP サーブレットを記述する際には、JNDI、JMS、EJB、JDBC 接続など、WebLogic Server の豊富な機能を利用できます。

以下のマニュアルには、これらの機能の詳細が記載されています。

データベースへのアクセス

WebLogic Server は、サーバサイド Java クラス (サーブレットなど) からの Java Database Connectivity (JDBC) の使用をサポートしています。JDBC を使うと、Java クラスから SQL クエリを実行し、クエリの結果を処理できます。JDBC および WebLogic Server の詳細については、『Oracle Fusion Middleware Oracle WebLogic Server JDBC プログラマーズ ガイド』を参照してください。

以下の節で説明するように、JDBC はサーブレットで使用できます。

DataSource オブジェクトを用いたデータベースへの接続

DataSource は、接続プールを参照するサーバサイド オブジェクトです。接続プールの登録により、JDBC ドライバ、データベース、ログインなど、データベース接続と関連するパラメータが定義されます。DataSource オブジェクトおよび接続プールは、Administration Console で作成します。


ヒント :

J2EE 準拠のアプリケーションを作成する場合は、DataSource オブジェクトの使用をお勧めします。

サーブレットで DataSource を使用する

  1. Administration Console を使って接続プールを登録します。詳細については、Oracle Fusion Middleware Oracle WebLogic Server の Administration Console オンライン ヘルプの「JDBC データ ソース : コンフィグレーション : 接続プール」を参照してください。

  2. 接続プールを指す DataSource オブジェクトを登録します。

  3. JNDI ツリーで、DataSource オブジェクトをルックアップします。次に例を示します。

    Context ctx = null;
    // JNDI ルックアップのためのコンテキストを取得する
    ctx = new InitialContext(ht);
    // DataSource オブジェクトをルックアップする
    javax.sql.DataSource ds
            = (javax.sql.DataSource) ctx.lookup ("myDataSource");
    
  4. DataSource を使用して、JDBC 接続を作成します。次に例を示します。

    java.sql.Connection conn = ds.getConnection();
    
  5. 接続を使用して、SQL 文を実行します。次に例を示します。

    Statement stmt = conn.createStatement();
    stmt.execute("select * from emp");
    . . .
    

JDBC ドライバを用いたデータベースへの直接接続

データベースへの直接接続は、データベース接続を確立する方法としては、最も効率の悪いものです。要求ごとに新しいデータベース接続を確立しなければならないからです。データベースへの接続には、どの JDBC ドライバも使用できます。Oracle では、Oracle および Microsoft SQL Server 用の JDBC ドライバを提供しています。詳細については、『Oracle Fusion Middleware Oracle WebLogic Server JDBC プログラマーズ ガイド』を参照してください。

HTTP サーブレットにおけるスレッドの問題

サーブレットの設計時に、高い負荷のもとで、WebLogic Server がサーブレットをどのように呼び出すか検討する必要があります。複数のクライアントが同時にサーブレットをヒットすることは避けられません。したがって、サーブレットのコードは、共有リソースやインスタンス変数の共有違反を防ぐように記述します。

共有リソースの問題は、個々のサーブレットごとに処理することをお勧めします。次のガイドラインを念頭に置いてください。

別のリソースへの要求のディスパッチ

この節では、要求をサーブレットから別のリソースへディスパッチするのによく使用されるメソッドの概要を説明します。

サーブレットでは、要求を別のリソース (サーブレット、JSP、または HTML ページなど) に渡すことができます。このプロセスは、要求のディスパッチと呼ばれます。要求をディスパッチする場合は、RequestDispatcher インタフェースの include() メソッドまたは forward() メソッドを使用します。

要求のディスパッチに関する詳細な説明については、Sun Microsystems が提供する Servlet 2.4 仕様 (http://java.sun.com/products/servlet/download.html#specs を参照) のセクション 8.2 を参照してください。

RequestDispatcher を使用すると、HTTP リダイレクト応答をクライアントに送り返す必要がなくなります。RequestDispatcher は、HTTP リクエストを要求されたリソースに渡します。

リソースを特定のリソースにディスパッチするには、以下の手順に従います。

  1. 次のように、ServletContext への参照を取得します。

    ServletContext sc = getServletConfig().getServletContext();
    
  2. 以下のメソッドの 1 つを用いて、RequestDispatcher オブジェクトをルックアップします。

    • RequestDispatcher rd = sc.getRequestDispatcher(String path);

    • path は、Web アプリケーションのルートに対する相対パスでなければなりません。

    • RequestDispatcher rd = sc.getNamedDispatcher(String name);

      name を J2EE 標準 Web アプリケーションのデプロイメント記述子 web.xml の中で <servlet-name> 要素によってそのサーブレットに割り当てられた名前で置き換えます。

    • RequestDispatcher rd = ServletRequest.getRequestDispatcher(String path);

      このメソッドは RequestDispatcher オブジェクトを返すものであって、ServletContext.getRequestDispatcher(String path) メソッドに似ています。ただし、ここでは、path を現在のサーブレットに対して相対的になるように指定することができます。「/」記号で始まるパスは、Web アプリケーションに対して相対的になるように解釈されます。

      HTTP サーブレット、JSP ページ、通常の HTML ページなど、Web アプリケーション内のどの HTTP リソースについても、getRequestDispatcher() メソッドでリソースの適切な URL を要求することによって、RequestDispatcher を取得できます。返された RequestDispatcher オブジェクトを使用して、要求を別のサーブレットに転送します。

  3. 適切なメソッドを使用して、要求を転送またはインクルードします。

要求の転送

一度、正しい RequestDispatcher が得られると、サーブレットは、引数として、HTTPServletRequestHTTPServletResponse を渡し、RequestDispatcher.forward() メソッドを使用して、要求を転送します。出力がすでにクライアントに送られた状態でこのメソッドを呼び出すと、IllegalStateException が送出されます。応答バッファの中に、コミットされていない保留中の出力がある場合には、バッファはリセットされます。

サーブレットは、応答に対する以前の出力を書き込もうとしてはいけません。要求を転送する前に、応答のためにサーブレットが ServletOutputStream または PrintWriter を取得すると、IllegalStateException が送出されます。

元のサーブレットからのそれ以外の出力はすべて、要求が転送された後は無視されます。

どのタイプの認証を使用する場合でも、転送された要求は、デフォルトではユーザに再認証を要求しません。この動作を変更して、転送された要求の認証を実行するには、<check-auth-on-forward/> 要素を WebLogic 固有のデプロイメント記述子 (weblogic.xml) の <container-descriptor> 要素に追加します。次に例を示します。

<container-descriptor>
    <check-auth-on-forward/>
</container-descriptor>

要求のインクルード

サーブレットでは、RequestDispatcher.include() メソッドを使用して、引数として HTTPServletRequestHTTPServletResponse を渡すことにより、他のリソースからの出力をインクルードすることができます。その場合、インクルードされたリソースは、要求オブジェクトにアクセスできます。

インクルードされたリソースは、応答オブジェクトの ServletOutputStream または Writer オブジェクトにデータを書き戻すことができ、その後、応答バッファにデータを追加するか、または応答オブジェクトに対し flush() メソッドを呼び出すかのいずれかを行うことができます。応答のステータス コード、またはインクルードされたサーブレットの応答からの HTTP ヘッダ情報を設定しようとすると、すべて無視されます。

実際には、include() メソッドを使用して、サーブレット コードから他の HTTP リソースの「サーバサイドインクルード」を実現できます。

RequestDispatcher とフィルタ

Sun Microsystems の Servlet 2.3 仕様では、転送やインクルードに対してフィルタを適用するかどうかを指定していませんでした。Servlet 2.4 仕様では、web.xml デプロイメント記述子に新しく dispatcher 要素を導入することにより、これを明確化しています。この dispatcher 要素を使用することで、REQUEST/FORWARD/INCLUDE/ERROR に対して適用されるフィルタ マッピング (filter-mapping) をコンフィグレーションできます。WebLogic Server 8.1 では、デフォルトは REQUEST+FORWARD+INCLUDE でした。古い DTD ベースのデプロイメント記述子については、下位互換性を維持するため、デフォルト値は変更されていません。新しい記述子 (スキーマ ベース) については、デフォルトは REQUEST です。

ディスパッチされた要求のデフォルトの動作は、weblogic.xmlfilter-dispatched-requests-enabled 要素を設定することで、変更できます。この要素は、ディスパッチされた (転送またはインクルード) 要求にフィルタを適用するかどうかを制御するものです。古い DTD ベースのデプロイメント記述子のデフォルト値は true です。新しいスキーマ ベースの記述子のデフォルトは false です。

RequestDispatcher およびフィルタの詳細については、http://java.sun.com/products/servlet/download.html#specs の Servlet 2.4 仕様のセクション 6.2.5 を参照してください。WebLogic Server 用のフィルタの記述とコンフィグレーションの詳細については、「フィルタ」を参照してください。

別の Web サーバへの要求のプロキシ

以下の節では、別の Web サーバに HTTP リクエストをプロキシする方法について説明します。

別の Web サーバへの要求のプロキシの概要

WebLogic Server をプライマリ Web サーバとして使用する場合、特定の要求をセカンダリ Web サーバ (Netscape Enterprise Server、Apache、Microsoft Internet Information Server など) に引き渡す (プロキシする) ように WebLogic Server をコンフィグレーションする必要があることもあります。プロキシされる要求はすべて、特定の URL にリダイレクトされます。要求は、別のマシン上の別の Web サーバにプロキシすることもできます。プロキシは、受信する要求の URL に基づいて行います。

HttpProxyServlet (配布キットの一部として提供) は、WebLogic Server を介して、HTTP リクエストを取得し、プロキシ URL にリダイレクトして、その応答をクライアントのブラウザに送信します。HttpProxyServlet を使用するには、Web アプリケーションでそれをコンフィグレーションして、要求をリダイレクトする WebLogic Server にその Web アプリケーションをデプロイします。

セカンダリ Web サーバへのプロキシの設定

セカンダリ HTTP サーバのプロキシを設定するには、次の手順に従います。

  1. プロキシ サーブレットを Web アプリケーション デプロイメント記述子に登録します (「ProxyServlet と共に使用する web.xml のサンプル」を参照)。Web アプリケーションは、要求に応答するサーバ インスタンスのデフォルト Web アプリケーションでなければなりません。プロキシ サーブレットのクラス名は、weblogic.servlet.proxy.HttpProxyServlet です。

  2. <param-name>redirectURL を指定し、<param-value> にプロキシされる要求のリダイレクト先サーバの URL を指定して、ProxyServlet の初期化パラメータを定義します。

  3. 独自の ID 証明書とキーで双方向 SSL を使用する場合は、必要に応じて以下の <KeyStore> 初期化パラメータを定義します。デプロイメント記述子に <KeyStore> が指定されていない場合、プロキシでは一方向 SSL と見なされます。

    • <KeyStore> - Web アプリケーションのキーストアの場所。

    • <KeyStoreType> - キーストアのタイプ。定義されていない場合、デフォルトのタイプが使用されます。

    • <PrivateKeyAlias> - プライベート キーのエリアス。

    • <KeyStorePasswordProperties> - Web アプリケーション内のプロパティ ファイル。キーストアおよびプライベート キーのエリアスにアクセスするための暗号化されたパスワードを定義します。このファイルの内容は次のとおりです。

      KeyStorePassword={3DES}i4+50LCKenQO8BBvlsXTrg\=\=
      PrivateKeyPassword={3DES}a4TcG4mtVVBRKtZwH3p7yA\=\=
      

      パスワードを暗号化するには、weblogic.security.Encrypt コマンドライン ユーティリティを使用する必要があります。Encrypt ユーティリティや、CertGen および der2pem ユーティリティの詳細については、『Oracle Fusion Middleware Oracle WebLogic Server コマンド リファレンス』の「WebLogic Server Java ユーティリティの使い方」を参照してください。

  4. ProxyServlet<url-pattern> にマップします。特に、プロキシするファイルの拡張子 (*.jsp*.html など) をマップします。Web アプリケーション デプロイメント記述子 web.xml<servlet-mapping> 要素を使用します。

    <url-pattern> を「/」に設定した場合、WebLogic Server によって解決できない要求はすべてリモート サーバにプロキシされます。しかし、拡張子が *.jsp*.htm、および *.html のファイルをプロキシする場合、これらの拡張子もマップしなければなりません。

  5. 受信する要求をリダイレクトする WebLogic Server インスタンスに Web アプリケーションをデプロイします。

プロキシ サーブレットのデプロイメント記述子のサンプル

次に、ProxyServlet を使用するための Web アプリケーション デプロイメント記述子のサンプルを示します。

コード リスト 9-2 ProxyServlet と共に使用する web.xml のサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://java.sun.com/xml/ns/j2ee"
      xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      elementFormDefault="qualified"
      attributeFormDefault="unqualified"
      version="2.4">
<web-app>
<servlet>
  <servlet-name>ProxyServlet</servlet-name> 
  <servlet-class>weblogic.servlet.proxy.HttpProxyServlet</servlet-class> 
  <init-param>
    <param-name>redirectURL</param-name>
    <param-value>http://server:port</param-value> 
  </init-param>
  <init-param>
    <param-name>KeyStore</param-name>
    <param-value>/mykeystore</param-value>
  </init-param>
  <init-param>
    <param-name>KeyStoreType</param-name>
    <param-value>jks</param-value>
  </init-param>
  <init-param>
    <param-name>PrivateKeyAlias</param-name>
    <param-value>passalias</param-value>
  </init-param>
  <init-param>
    <param-name>KeyStorePasswordProperties</param-name>
    <param-value>mykeystore.properties</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>ProxyServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>ProxyServlet</servlet-name> 
  <url-pattern>*.jsp</url-pattern> 
</servlet-mapping>
<servlet-mapping>
  <servlet-name>ProxyServlet</servlet-name> 
  <url-pattern>*.htm</url-pattern> 
</servlet-mapping>
<servlet-mapping>
  <servlet-name>ProxyServlet</servlet-name> 
  <url-pattern>*.html</url-pattern> 
</servlet-mapping>
</web-app>

サーブレットのクラスタ化

サーブレットをクラスタ化すると、フェイルオーバとロード バランシングのメリットを活かせます。WebLogic Server のクラスタにサーブレットをデプロイするには、サーブレットを含む Web アプリケーションをクラスタ内の全サーバにデプロイします。

サーブレットのクラスタ化に関する要件と、クラスタ化されたサーブレットに送られる要求の接続およびフェイルオーバ プロセスの詳細については、『Oracle Fusion Middleware Oracle WebLogic Server クラスタの使い方』の「サーブレットと JSP のレプリケーションとフェイルオーバ」を参照してください。


注意 :

サーブレットの自動フェイルオーバには、サーブレット セッション ステートをメモリ内にレプリケートする必要があります。手順については、『Oracle Fusion Middleware Oracle WebLogic Server クラスタの使い方』の「インメモリ HTTP レプリケーションをコンフィグレーションする」を参照してください。

サーブレットに関して WebLogic Server クラスタがサポートしているロード バランシングの詳細と、関連するプランニングとコンフィグレーションに関する開発者および管理者向け考慮事項については、『Oracle Fusion Middleware Oracle WebLogic Server クラスタの使い方』の「サーブレットと JSP のロード バランシング」を参照してください。

Web アプリケーションでのサーブレットの参照

Web アプリケーションでサーブレットを参照するための URL は、次のように構成されます。

http://myHostName:port/myContextPath/myRequest/myRequestParameters

URL の各要素は次のように定義します。

URL パターン マッチング

WebLogic Server には、J2EE のマッチング ルールに適合していない URL マッチング ユーティリティを実装する機能があります。マッチング ユーティリティは、デフォルトで実装される URLMatchMap とは異なり、web.xml デプロイメント記述子ではなく、weblogic.xml デプロイメント記述子でコンフィグレーションする必要があります。

URL マッチング ユーティリティを WebLogic Server で使用するには、次のインタフェースを実装する必要があります。

Package weblogic.servlet.utils;
public interface URLMapping {
  public void put(String pattern, Object value);
  public Object get(String uri);
  public void remove(String pattern)
  public void setDefault(Object defaultObject);
  public Object getDefault();
  public void setCaseInsensitive(boolean ci);
  public boolean isCaseInsensitive();
  public int size();
  public Object[] values();
  public String[] keys();
}

SimpleApacheURLMatchMap ユーティリティ

同梱されている SimpleApacheURLMatchMap ユーティリティは、J2EE 固有のユーティリティではありません。このユーティリティは、weblogic.xml デプロイメント記述子ファイルでコンフィグレーションします。このユーティリティを使用すると、ユーザは、web.xml デプロイメント記述子に指定されたデフォルトの URL パターン マッチングではなく、Apache スタイルのパターン マッチングを指定することができます。詳細については、「url-match-map」を参照してください。

HTTP サーブレットの将来的応答モデル

一般的に、WebLogic Server が着信する HTTP リクエストを処理すると、応答は即座にクライアントに返されます。そのような接続は、同じスレッドによって同期的に処理されます。しかし、一部の HTTP リクエストでは、より長い処理時間が必要となることがあります。たとえばデータベース接続で生じる応答時間は、より長くなる場合があります。これらの要求の同期的な処理では、その要求が処理されて応答が送信されるまで待機が行われ、スレッドが保持されます。

このようにスレッドがハングしてしまう状況を回避するため、WebLogic Server では、着信する要求を処理するスレッドから応答を切り離すことによって、HTTP リクエストを非同期に処理する、2 つのクラスが用意されています。以下の節で、これらのクラスについて説明します。

抽象非同期サーブレット

抽象非同期サーブレットを使うと、着信する要求とサーブレット応答を、別々のスレッドで処理できます。このクラスはスレッド処理を含めて、将来的応答サーブレットよりも応答を処理するための全般的なフレームワークが優れています。

抽象非同期サーブレットを実装するには、weblogic.servlet.http.AbstractAsyncServlet.java クラスを拡張します。このクラスにより、拡張クラスでオーバーライドする必要のある以下の抽象メソッドが提供されます。

doRequest

このメソッドは、サーブレット要求を処理します。次のサンプル コードでは、このメソッドをオーバーライドする方法を示します。

コード リスト 9-3 AbstractAsynchServlet.java の doRequest のオーバーライド

public boolean doRequest(RequestResponseKey rrk) 
      throws ServletException, IOException {
      HttpServletRequest req = rrk.getRequest();
      HttpServletResponse res = rrk.getResponse();

      if (req.getParameter("immediate") != null) {
            res.setContentType("text/html");
            PrintWriter out = res.getWriter();
            out.println("Hello World Immediately!");
            return false ;
      }
      else {
            TimerManagerFactory.getTimerManagerFactory()
            .getDefaultTimerManager().schedule
            (new TimerListener() {
                  public void timerExpired(Timer timer)
                        {try {
                              AbstractAsyncServlet.notify(rrk, null);
                        }
                        catch (Exception e) {
                              e.printStackTrace();
                        }
                  }
            }, 2000);
      return true;
      }
}

doResponse

このメソッドは、サーブレット応答を処理します。


注意 :

元の着信要求メソッドの処理に使用される doRequest() メソッドを処理していたサーブレット インスタンスが、必ずしも doResponse() メソッドを処理するとは限りません。

処理中に例外が発生した場合、コンテナはクライアントにエラーを返します。次のサンプル コードでは、このメソッドをオーバーライドする方法を示します。

コード リスト 9-4 AbstractAsyncServlet.java の doResponse() のオーバーライド

public void doResponse (RequestResponseKey rrk, Object context)
   throws ServletException, IOException
      {
      HttpServletRequest req = rrk.getRequest();
      HttpServletResponse res = rrk.getResponse();

      res.setContentType("text/html");
      PrintWriter out = res.getWriter();
      out.println("Hello World!");
}

doTimeOut

このメソッドは、タイムアウト期間中に notify() メソッドが呼び出されなかった場合に、サーブレット応答エラーを送信します。


注意 :

元の着信要求メソッドの処理に使用される doRequest() メソッドを処理していたサーブレット インスタンスが、必ずしも doTimeOut() メソッドを処理するとは限りません。

コード リスト 9-5 AbstractAsyncServlet.java の doTimeOut() のオーバーライド

public void doTimeout (RequestResponseKey rrk)
      throws ServletException, IOException
{
      HttpServletRequest req = rrk.getRequest();
      HttpServletResponse res = rrk.getResponse();

      res.setContentType("text/html");
      PrintWriter out = res.getWriter();
      out.println("Timeout!");
}

将来的応答サーブレット

抽象非同期サーブレットを使用することをお勧めしますが、着信する要求を処理するスレッドとは別のスレッドでサーブレット応答を処理するには、将来的応答サーブレットを使用できます。このサーブレットを有効化するには、weblogic.servlet.FutureResponseServlet.java を拡張します。これにより、応答の処理方法が完全に制御され、スレッドの処理をより詳細に管理できます。ただし、このクラスを使用してスレッドのハングを防ぐためには、コードのほとんどを指定する必要があります。

正確な実装は、ニーズに応じて変わりますが、少なくともこのクラスの service() メソッドをオーバーライドする必要があります。以下の例では、サービス メソッドをオーバーライドする方法を示します。

コード リスト 9-6 FutureResponseServlet.java の service() メソッドのオーバーライド

  public void service(HttpServletRequest req, FutureServletResponse rsp)
    throws IOException, ServletException {
    if(req.getParameter("immediate") != null){
      PrintWriter out = rsp.getWriter();
      out.println("Immediate response!");
      rsp.send();
    } else {
      Timer myTimer = new Timer();
      MyTimerTask mt = new MyTimerTask(rsp, myTimer);
      myTimer.schedule(mt, 100);
    } 
  }

  private static class MyTimerTask extends TimerTask{
    private FutureServletResponse rsp;
    Timer timer;
    MyTimerTask(FutureServletResponse rsp, Timer timer){
      this.rsp = rsp;
      this.timer = timer;
    }
    public void run(){
      try{
        PrintWriter out = rsp.getWriter();
        out.println("Delayed Response");
        rsp.send();
        timer.cancel();
      }
      catch(IOException e){
        e.printStackTrace();
      }
    }
  }