8 サーブレット・プログラミング・タスク
この章の内容は以下のとおりです。
サーブレットの初期化
destroy()
メソッドが呼び出されます。変更後のサーブレットにリクエストが送信されると、変更後のサーブレットのinit()
メソッドが実行されます。 「サーブレットのベスト・プラクティス」を参照してください。
サーブレットが初期化されるとき、WebLogic Serverはサーブレットのinit()
メソッドを実行します。サーブレットは一度初期化されると、WebLogic Serverを再起動するまで、またはサーブレット・コードが変更されるまで、再び初期化されることはありません。init()
メソッドをオーバーライドすると、データベース接続の確立など、サーブレットの初期化時に特定のタスクを実行させることができます(「init()メソッドのオーバーライド」を参照。)
WebLogic Server起動時のサーブレットの初期化
サーブレットに最初のリクエストが送信された時にWebLogic Serverがサーブレットを初期化するのではなく、サーバーの起動時に初期化するように、最初にWebLogic Serverを構成できます。これを行うには、Java EE標準のWebアプリケーション・デプロイメント記述子web.xml
のload-on-startup
要素内にサーブレット・クラスを指定します。Webアプリケーション内でのリソースの初期化の順序は、次のとおりです。
-
ServletContextListeners
- このWebアプリケーションについて登録されたServletContextListeners
に対するcontextCreated()
コールバック。 -
ServletFilters init()
メソッド。 -
Servlet init()
メソッド(web.xml
内ではload-on-startup
としてマーク)。
初期化中にHTTPサーブレットにパラメータを渡すには、サーブレットを含むWebアプリケーションでパラメータを定義します。このパラメータを使用すると、サーブレットが初期化されるたびに書き換えることなくサーブレットへ値を渡せます。
たとえば、Java EE標準のWebアプリケーション・デプロイメント記述子web.xml
内に次のようなエントリを作成すると、Welcome
の値を持つgreeting
およびWebLogic Developer
の値を持つperson
の2つの初期化パラメータが定義されます。
<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()
メソッドをオーバーライドすることで、初期化時にサーブレットにタスクを実行させることができます。次のコードでは、Java EE標準の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
オブジェクトを使用して渡さなければなりません。
-
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");
-
-
HTMLページを作成します。
サーブレットがクライアントに送り返す応答は、通常のHTTPコンテンツ(基本的にはHTML形式)のように見える必要があります。サーブレットは、
service()
メソッドの応答パラメータを使用して取得した出力ストリームを通じて、HTTP応答を送り返します。HTTPレスポンスを送信するには:-
HttpServletResponse
オブジェクトおよび次の例のいずれかのメソッドを使用して、出力ストリームを取得します。-
PrintWriter out = res.getWriter();
-
ServletOutputStream out = res.getOutputStream();
-
-
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接続の利点を活かすことができます。
-
-
レスポンスを最適化します。
デフォルトでは、WebLogic Serverは、可能な限りHTTPの永続的接続を使用しようとします。永続的接続は、クライアントとサーバー間の一連の通信のために、同一のHTTP TCP/IP接続を再利用しようとします。各リクエストでは新規接続を開く必要はないため、アプリケーションのパフォーマンスが向上します。永続的接続は、HTMLページにインライン画像が多く含まれる場合に便利です。そうしないと、画像が要求されるごとに新しいTCP/IP接続が必要になるためです。
WebLogic Server 管理コンソールを使用すると、WebLogic ServerがHTTP接続をオープンに保つ時間を構成できます。
永続的接続を確立するために、WebLogic ServerはHTTPレスポンスの長さを知る必要があるので、HTTPレスポンス・ヘッダーに
Content-Length
プロパティを自動的に追加します。コンテンツ長を確認するために、WebLogic Serverではレスポンスをバッファリングする必要があります。ただし、サーブレットがServletOutputStream
を明示的にフラッシュすると、WebLogic Serverではレスポンスの長さを判別できないため、永続的接続を使用できません。このため、サーブレットでHTTPレスポンスを明示的にフラッシュすることは避けます。場合によっては、ページ完成前にクライアントに情報を表示するためにレスポンスを早くフラッシュした方がよいこともあります。たとえば、時間のかかるページのコンテンツを計算している途中で、バナー広告を表示する場合などです。逆に、サーブレット・エンジンが使用するバッファ・サイズを増やして、フラッシュする前に、もっと長いレスポンスを入れておきたいという場合もあります。
javax.servlet.ServletResponse
インタフェースの関連メソッドを使用して、レスポンス・バッファのサイズを操作することができます。サーブレット3.1仕様(http://jcp.org/en/jsr/detail?id=340
)を参照してください。WebLogic Serverのレスポンス・バッファのデフォルト値は12Kで、バッファ・サイズは、
CHUNK_SIZE
(CHUNK_SIZE = 4088
CHUNK_SIZE
の倍数(この場合は2)である8176バイトに設定されます。
クライアント入力の取得
HTTPサーブレットAPIは、Webページからユーザー入力を取得するためのインタフェースを提供しています。
WebブラウザからのHTTPリクエストには、クライアント、ブラウザ、Cookie、およびユーザーの問合せパラメータに関する情報といった、URL以外の情報も含めることができます。ブラウザからのユーザー入力を渡すには、問合せパラメータを使用します。GET
メソッドはURLアドレスにパラメータを付加し、POST
メソッドはそれらをHTTPリクエスト本文の中に含めます。
HTTPサーブレットでは、これらの細部を扱う必要はありません。リクエストの中の情報は、送った方法に関係なく、HttpServletRequest
オブジェクトを通じて取得され、request.getParameter()
メソッドを使用してアクセスできるようになります。
問合せパラメータをクライアントから送る方法については、次を参照してください。
-
ページのリンクのURLに、パラメータを直接エンコードします。この方法では、
GET
メソッドを使用してパラメータを送ります。パラメータはURLの後ろに?
を付けてその後に追加します。パラメータが複数ある場合は&
で区切ります。パラメータは、常に名前=値のペアで指定されます。指定される順序は重要ではありません。たとえば、次のようなリンクをWebページに入れる場合、ここではColorServlet
と呼ばれるHTTPサーブレットに、パラメータcolorの値をpurple
にして送ります。<a href= "http://localhost:7001/myWebApp/ColorServlet?color=purple"> Click Here For Purple!</a>
-
問合せパラメータを付けたURLをブラウザの場所フィールドに手動で入力します。これは前の例で示したリンクをクリックするのと同じことです。
-
HTMLフォームで、ユーザーに入力を要求します。このフォームの送信ボタンがクリックされると、フォーム上の各ユーザー入力フィールドの内容が、問合せパラメータとして送られます。このフォームが問合せパラメータを送るために使用するメソッド(
POST
またはGET
)を、METHOD="GET|POST"
属性を使用して<FORM>
タグに指定します。
問合せパラメータは常に名前=値のペアで送られ、HttpServletRequest
オブジェクトを通じてアクセスされます。問合せのすべてのパラメータ名のEnumeration
を取得し、そのパラメータ名を使用して各パラメータの値を取得できます。1つのパラメータの値は通常1つのみですが、値の配列を持つこともできます。パラメータの値は常にString
として読み取られるので、より適切な型にキャストすることが必要な場合もあります。
service()
メソッドからの次のサンプルでは、フォームから得た問合せパラメータ名とその値を調べます。request
はHttpServletRequest
オブジェクトであることに注意してください。
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()
-GET
やPOST
などのリクエスト・メソッドを決定できます。 -
HttpServletRequest.getQueryString()
- 問合せ文字列へのアクセスを可能にします。(?
の後に続く、要求されたURLの残りの部分。) -
HttpServletRequest.getParameter()
- パラメータの値を返します。 -
HttpServletRequest.getParameterNames()
- パラメータ名の配列を返します。 -
HttpServletRequest.getParameterValues()
- パラメータの値の配列を返します。 -
HttpServletRequest.getInputStream()
—リクエスト本体をバイナリ・データとして読み込みます。要求パラメータをgetParameter()
、getParameterNames()
またはgetParameterValues()
で読み込んだ後にこのメソッドを呼び出すと、IllegalStateException
がスローされます。
例: 問合せパラメータによる入力の取得
例8-1では、より個人的な挨拶文を表示するために、問合せパラメータとしてユーザー名を受け付けるようにHelloWorld2.java
サーブレットのサンプルを修正しています。service()
メソッドを次に示します。
例8-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; } // Set the content type first res.setContentType("text/html"); // Obtain a PrintWriter as an output stream 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) { // Now you can use the myStr[0]; } else { // paramName was not in the query parameters! }
サーブレットでのクライアント入力のセキュリティ
ユーザーが入力したデータを取得して返す機能があると、クロス・サイト・スクリプティングと呼ばれるセキュリティの脆弱性がもたらされます。これは、ユーザーのセキュリティ認可を盗用するために利用される可能性があります。
クロス・サイト・スクリプティングの詳細については、http://www.cert.org/tech_tips/malicious_code_mitigation.html
の「Understanding Malicious Content Mitigation for Web Developers」(CERTのセキュリティ勧告)を参照してください。
セキュリティの脆弱性をなくすには、ユーザーが入力したデータを戻す前に、そのデータをスキャンして、表8-1に示すHTML特殊文字があるかどうかを調べます。特殊文字が見つかった場合、HTMLのエンティティ参照または文字参照と置き換えます。文字を置換することで、ブラウザでユーザー入力によるデータがHTMLとして実行されることを回避します。
表8-1 置換えの必要があるHTMLの特殊文字
置き換える特殊文字 | 置換え後のエンティティ/文字参照 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
WebLogic Serverユーティリティ・メソッドを使用する
WebLogic Serverには、ユーザーが入力したデータ内の特殊文字を置き換えるweblogic.servlet.security.Utils.encodeXSS()
メソッドが用意されています。このメソッドを使用するには、ユーザー入力データを入力として提供します。たとえば、例8-1でユーザーが指定したデータを保護するには、次の行を置換します:
out.print(defaultGreeting + " " + name + "!");
を、以下の行と置き換えます。
out.print(defaultGreeting + " " + weblogic.security.servlet.encodeXSS(name) + "!");
アプリケーション全体を保護するため、ユーザーが入力したデータを戻すたびに
、encodeXSS()メソッドを使用する必要があります。前述の例8-1での例は、encodeXSS()
メソッドを使用すべき場所の1つですが、表8-2ではこのメソッドを使用すべきかどうかを検討すべきその他の場所を示します。
表8-2 ユーザー入力データを戻すコード
ページのタイプ | ユーザー入力データ | 例 |
---|---|---|
エラー・ページ |
誤りのある入力文字列、無効なURLやユーザー名 |
ユーザー名はアクセスを許可されていませんと表示するエラー・ページ |
ステータス・ページ |
ユーザー名、以前のページでの入力の要約 |
以前のページでの入力を確認するようユーザーに求める要約ページ |
データベース表示 |
データベースから提示されたデータ |
以前にユーザーによって入力されたデータベース・エントリのリストを表示するページ |
サーブレットでのCookieの使用方法
Cookieは、クライアントからサーバーに戻されるので、そのクライアントを識別するために便利なものです。ブラウザは、同じサーバーにアクセスするたび、HTTPリクエストと共にそのサーバーに関連したCookieをすべて送ります。Cookieは、クライアントからサーバーに戻されるので、そのクライアントを識別するために便利なものです。
各Cookieは名前と値を保持しています。通常、Cookieをサポートしているブラウザでは、各サーバーのドメインに、1つあたり最大4KのCookieを20まで格納することができます。
HTTPサーブレットでのCookieの設定
ブラウザ上にCookieを設定するには、Cookieを作成し、値を与え、サーブレットのサービス・メソッドの2番目のパラメータであるHttpServletResponse
オブジェクトに追加します。たとえば:
Cookie myCookie = new Cookie("ChocolateChip", "100"); myCookie.setMaxAge(Integer.MAX_VALUE); response.addCookie(myCookie);
上記の例では、値が100
のChocolateChip
と呼ばれるCookieを、レスポンスの送信時にブラウザ・クライアントに追加します。Cookieの有効期限は指定できる最大値に設定されているため、Cookieは永久に有効です。Cookieは文字列型の値のみを受け入れるので、Cookieに格納するための必要な型との間でキャストします。EJBの場合、一般的にはCookieの値に対するEJBインスタンスのホーム・ハンドルを使用し、EJBにユーザーの詳細情報を格納して、後で参照できるようにします。
HTTPサーブレットでのCookieの取得
service()
メソッドへの引数としてサーブレットに渡されるHttpServletRequest
から、Cookieオブジェクトを取得することができます。Cookieそのものは、javax.servlet.http.Cookie
オブジェクトとして示されます。
サーブレット・コードでは、getCookies()
メソッドを呼び出すことにより、ブラウザから送られたすべてのCookieを取得することができます。たとえば:
Cookie[] cookies = request.getCookies();
このメソッドは、ブラウザから送られたすべてのCookieの配列を戻すか、またはブラウザから送られたCookieがない場合、null
を返します。サーブレットは、その配列を処理して正しい名前のCookieを探す必要があります。Cookieの名前は、Cookie.getName()
メソッドを使って取得することができます。同一の名前で、パス属性の異なるCookieが複数ある可能性があります。サーブレットが、同一の名前でパス属性の異なる複数のCookieを設定した場合、Cookie.getPath()
メソッドを使ってこれらを比較することも必要です。以下のコードは、ブラウザから送られたCookieの詳細にアクセスする方法を示しています。このサーバーに送られたCookieはすべて一意の名前を持ち、ブラウザ・クライアントで以前に設定したと考えられるChocolateChip
というCookieを探しているという前提です。
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) { // We found the cookie! Now get its value int cookieOrder = String.parseInt(thisCookie.getValue()); }
HTTPとHTTPSの両方で送信されるCookieの使用方法
HTTPリクエストとHTTPSリクエストは異なるポートに送られるので、ブラウザによっては、HTTPリクエストに入れて送られてきたCookieを、その後続のHTTPSリクエストに包含しない(あるいはその逆)ことがあります。このような場合には、サーブレット・リクエストがHTTPとHTTPSの間で切り替わると、新しいセッションが作成されることになります。セッション内でリクエストが行われるたびに、特定のドメインによって設定されるすべてのCookieがサーバーに送られるようにするには、cookie-domain
要素をドメイン名に設定します。cookie-domain
要素は、WebLogic固有のデプロイメント記述子weblogic.xml
のsession-descriptor
要素の下位要素です。たとえば:
<session-descriptor> <cookie-domain>example.com</cookie-domain> </session-descriptor>
cookie-domain
要素は、example.com
によって指定されたドメイン内のホストに、すべてのリクエストについて適切なCookieを入れるようブラウザに指示します。このプロパティやセッションCookieの構成の詳細は、「セッション管理の設定」 を参照してください。
アプリケーションのセキュリティとCookie
Cookieの使用は、マシン上での自動的なアカウント・アクセスを可能にして便利ですが、セキュリティの観点からは望ましくない場合があります。Cookieを使用するアプリケーション設計時には、以下のガイドラインに従ってください。
-
Cookieが常にユーザーに対して正確であると考えないようにします。マシンが共有されている場合や、同一のユーザーが異なるアカウントにアクセスしようとする場合もあります。
-
ユーザーが、サーバー上にCookieを残すかどうか選択できるようにします。共有マシンでは、ユーザーがそのアカウントに対する自動的なログインをそのままにしておくことを望まない場合があります。ユーザーがCookieについて理解していると仮定せずに、以下のような質問をします。
Automatically login from this computer?
-
機密データを取得するためにログオンするユーザーに対しては、常にパスワード入力を求めます。ユーザーが拒否しない限り、このプリファレンスとパスワードをユーザーのセッション・データに格納できます。セッションのCookieは、ユーザーがブラウザを終了したときに期限切れになるように構成します。
レスポンスのキャッシュ
キャッシュ・フィルタは、特定の例外を除いて、キャッシュ・タグと同様に動作します。
-
JSPフラグメント・レベルではなく、ページ・レベル(インクルードされたページ)でキャッシュします。
-
ドキュメント内でキャッシュ・パラメータを宣言するかわりに、Webアプリケーションの構成内でパラメータを宣言できます。
キャッシュ・フィルタには、別のページに含まれていなかったページのためのキャッシュ・タグにはないデフォルト動作があります。キャッシュ・フィルタは、レスポンス・ヘッダーContent-TypeとLast-Modifiedを自動的にキャッシュします。キャッシュ・フィルタは、キャッシュ内に存在しているページのリクエストを受け取ると、リクエストのIf-Modified-SinceヘッダーとLast-Modifiedレスポンス・ヘッダーを比較して、実際にコンテンツを提供するか、302 SC_NOT_MODIFED
ステータスと空のコンテンツを送信するかを決定します。
次の例は、Java EE標準のデプロイメント記述子web.xml
のfilter
要素を使用して、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
- キャッシュのスコープには、request、session、application、clusterのいずれかを指定できます。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 WebLogic Server JDBCアプリケーションの開発』を参照してください。
次の項で説明するように、JDBCはサーブレットで使用できます。
DataSourceオブジェクトを用いたデータベースへの接続
DataSource
は、接続プールを参照するサーバー側オブジェクトです。接続プールの登録により、JDBCドライバ、データベース、ログインなど、データベース接続と関連するパラメータが定義されます。DataSource
オブジェクトおよび接続プールは、管理コンソールで作成します。
ノート:
Java EE準拠のアプリケーションを作成する場合は、DataSource
オブジェクトの使用をお薦めします。
JDBCドライバを用いたデータベースへの直接接続
データベースへの直接接続は、データベース接続を確立する方法としては、最も効率の悪いものです。リクエストごとに新しいデータベース接続を確立しなければならないからです。データベースへの接続には、どのJDBCドライバも使用できます。Oracleでは、OracleおよびMicrosoft SQL Server用のJDBCドライバを提供しています。『Oracle WebLogic Server JDBCアプリケーションの開発』を参照してください。
HTTPサーブレットにおけるスレッドの問題
サーブレットの設計時に、高い負荷のもとで、WebLogic Serverがサーブレットをどのように呼び出すか検討する必要があります。複数のクライアントが同時にサーブレットをヒットすることは避けられません。したがって、サーブレットのコードは、共有リソースやインスタンス変数の共有違反を防ぐように記述します。
共有リソースの問題は、個々のサーブレットごとに処理することをお薦めします。次のガイドラインを念頭に置いてください。
-
可能な限り、同期を避けます。引き続き発生するサーブレットのリクエストが、現行のスレッドが完了するまで、ボトルネックになるためです。
-
各サーブレット・リクエストに固有の変数は、サービス・メソッドのスコープ内で定義します。ローカル・スコープ変数は、スタックに保存されるため、同一のメソッド内で実行されている複数のスレッドで共有されることはありません。したがって、同期の必要性はなくなります。
-
外部リソースへのアクセスは、Classレベルで同期を取るか、トランザクションにカプセル化します。
別のリソースへのリクエストのディスパッチ
リクエストをサーブレットから別のリソースへディスパッチするのによく使用されるメソッドの概要を説明します。
サーブレットでは、リクエストを別のリソース(サーブレット、JSP、またはHTMLページなど)に渡すことができます。このプロセスは、リクエストのディスパッチと呼ばれます。リクエストをディスパッチする場合は、RequestDispatcher
インタフェースのinclude()
メソッドまたはforward()
メソッドを使用します。
リクエストのディスパッチの詳細は、サーブレット3.1仕様(http://jcp.org/en/jsr/detail?id=340
)の9.2項を参照してください。
RequestDispatcher
を使用すると、HTTPリダイレクト・レスポンスをクライアントに送り返す必要がなくなります。RequestDispatcher
は、リクエストされたリソースにHTTPリクエストを渡します。
リクエストを特定のリソースにディスパッチするには:
リクエストの転送
一度、正しいRequestDispatcher
が得られると、サーブレットは、引数として、HTTPServletRequest
とHTTPServletResponse
を渡し、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()
メソッドを使用して、引数としてHTTPServletRequest
とHTTPServletResponse
を渡すことにより、他のリソースからの出力をインクルードすることができます。その場合、インクルードされたリソースは、リクエスト・オブジェクトにアクセスできます。
インクルードされたリソースは、レスポンス・オブジェクトのServletOutputStream
またはWriter
オブジェクトにデータを書き戻すことができ、その後、レスポンス・バッファにデータを追加するか、またはレスポンス・オブジェクトに対しflush()
メソッドを呼び出すかのいずれかを行うことができます。レスポンスのステータス・コード、またはインクルードされたサーブレットのレスポンスからのHTTPヘッダー情報を設定しようとすると、すべて無視されます。
実際には、include()
メソッドを使用して、サーブレット・コードから他のHTTPリソースの「サーバー側インクルード」を実現できます。
RequestDispatcherとフィルタ
サーブレット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.xml
でfilter-dispatched-requests-enabled
要素を設定することで、変更できます。この要素は、ディスパッチされた(転送またはインクルード)リクエストにフィルタを適用するかどうかを制御するものです。古いDTDベースのデプロイメント記述子のデフォルト値はtrue
です。新しいスキーマ・ベースの記述子のデフォルトはfalse
です。
RequestDispatcherおよびフィルタの詳細は、サーブレット3.1仕様(http://jcp.org/en/jsr/detail?id=340
)を参照してください。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アプリケーションをデプロイします。
プロキシ・サーブレットのデプロイメント記述子のサンプル
ProxyServlet
を使用するためのWebアプリケーション・デプロイメント記述子のサンプルを次に示します。
例8-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 WebLogic Serverクラスタの管理』のサーブレットとJSPのレプリケーションとフェイルオーバーに関する項を参照してください。
ノート:
サーブレットの自動フェイルオーバーでは、サーブレット・セッション状態をメモリー内にレプリケートする必要があります。手順については、『Oracle WebLogic Serverクラスタの管理』のインメモリーHTTPレプリケーションを構成するに関する項を参照してください。
サーブレットに関してWebLogic Serverクラスタがサポートしているロード・バランシングと、関連するプランニングと構成に関する開発者および管理者向け考慮事項の詳細は、『Oracle WebLogic Serverクラスタの管理』のサーブレットとJSPのロード・バランシングに関する項を参照してください。
Webアプリケーションでのサーブレットの参照
Webアプリケーションでサーブレットを参照するために使用されるURLは、特定のパターンで構築されます。
http://myHostName:port
/myContextPath
/myRequest
/myRequestParameters
URLの各要素は次のように定義します。
-
myHostName
—WebLogic Server管理コンソールで定義される、WebLogic ServerにマップされるDNS名です。URLのこの部分は、host:port
に置き換えることができます。host
は、WebLogic Serverが実行されているマシン名、port
はWebLogic Serverがリクエストをリスニングしているポートです。 -
port
- WebLogic Serverがリクエストをリスニングしているポートです。サーブレットは、Server MBeanとSSL MBean上のlistenPortポートを通じてのみプロキシと通信できます。 -
myContextPath
-weblogic.xml
ファイルで指定されるコンテキスト・ルート名、またはconfig.xml
ファイルで指定されるWebモジュールのURIです。 -
myRequest
-web.xml
ファイルで定義されるサーブレット名です。 -
myRequestParameters
- URLにエンコードされるHTTPリクエスト・パラメータです(オプション)。HTTPサーブレットで読取り可能です。
URLパターン・マッチング
WebLogic Serverには、Java EEのマッチング・ルールに適合していない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
ユーティリティは、Java EE固有のユーティリティではありません。このユーティリティは、weblogic.xml
デプロイメント記述子ファイルで構成します。このユーティリティを使用すると、ユーザーは、web.xml
デプロイメント記述子に指定されたデフォルトのURLパターン・マッチングではなく、Apacheスタイルのパターン・マッチングを指定することができます。
「url-match-map」を参照してください。
HTTPサーブレットの将来的レスポンス・モデル
一般的に、WebLogic Serverが着信するHTTPリクエストを処理すると、レスポンスは即座にクライアントに返されます。そのような接続は、同じスレッドによって同期的に処理されます。しかし、一部のHTTPリクエストでは、より長い処理時間が必要となることがあります。たとえばデータベース接続で生じるレスポンス時間は、より長くなる場合があります。これらのリクエストの同期的な処理では、そのリクエストが処理されてレスポンスが送信されるまで待機が行われ、スレッドが保持されます。
このようにスレッドがハングしてしまう状況を回避するため、WebLogic Serverでは、着信するリクエストを処理するスレッドからレスポンスを切り離すことによって、HTTPリクエストを非同期に処理する、2つのクラスが用意されています。次の項で、これらのクラスについて説明します。
抽象非同期サーブレット
ノート:
WebLogic Server 12.1.3以降は、WebLogic Server抽象非同期サーブレットのかわりに、サーブレット3.1仕様で定義されている標準非同期処理モデルを使用することをお薦めします。
抽象非同期サーブレットを使用すると、着信するリクエストとサーブレット・レスポンスを、別々のスレッドで処理できます。このクラスはスレッド処理を含めて、将来的レスポンス・サーブレットよりもレスポンスを処理するための全般的なフレームワークが優れています。
抽象非同期サーブレットを実装するには、weblogic.servlet.http.AbstractAsyncServlet.java
クラスを拡張します。このクラスにより、拡張クラスでオーバーライドする必要のある次の抽象メソッドが提供されます。
doRequest
このメソッドは、サーブレット・リクエストを処理します。次のサンプル・コードでは、このメソッドをオーバーライドする方法を示します。
例8-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()
メソッドを処理するとは限りません。
処理中に例外が発生した場合、コンテナはクライアントにエラーを返します。次のサンプル・コードでは、このメソッドをオーバーライドする方法を示します。
例8-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()
メソッドを処理するとは限りません。
例8-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 Server 12.1.3以降は、サーブレット3.1仕様で定義されている標準非同期処理モデルを使用することをお薦めします。
また、将来的レスポンス・サーブレットを使用して、受信リクエストを処理するスレッドとは別のスレッドでサーブレット・レスポンスを処理することもできます。このサーブレットを有効化するには、weblogic.servlet.FutureResponseServlet.java
を拡張します。これにより、レスポンスの処理方法が完全に制御され、スレッドの処理をより詳細に管理できます。ただし、このクラスを使用してスレッドのハングを防ぐためには、コードのほとんどを指定する必要があります。
正確な実装は、ニーズに応じて変わりますが、少なくともこのクラスのservice()
メソッドをオーバーライドする必要があります。以下の例では、サービス・メソッドをオーバーライドする方法を示します。
例8-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(); } } }