Oracle® Fusion Middleware Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発 11gリリース1 (10.3.6) B60993-04 |
|
![]() 前 |
![]() 次 |
次の項では、WebLogic Server環境でHTTPサーブレットを記述する方法について説明します。
通常、WebLogic Serverによってサーブレットが初期化されるのは、そのサーブレットに対して最初のリクエストが出されたときです。その後、そのサーブレットが変更されると、既存のバージョンのサーブレットに対してdestroy()
メソッドが呼び出されます。変更後のサーブレットにリクエストが送信されると、変更後のサーブレットのinit()
メソッドが実行されます。詳細は、「サーブレットのベスト・プラクティス」を参照してください。
サーブレットが初期化されるとき、WebLogic Serverはサーブレットのinit()
メソッドを実行します。サーブレットは一度初期化されると、WebLogic Serverを再起動するまで、またはサーブレット・コードが変更されるまで、再び初期化されることはありません。init()
メソッドをオーバーライドすると、データベース接続の確立など、サーブレットの初期化時に特定のタスクを実行させることができます(「init()メソッドのオーバーライド」を参照)。
サーブレットに最初のリクエストが送信された時に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()
メソッドをオーバーライドすることで、初期化時にサーブレットにタスクを実行させることができます。次のコードでは、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サーブレットでクライアントへのレスポンスを提供する方法について説明します。レスポンスはすべて、サーブレットの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();
同じサーブレット内(または、サーブレットの中にインクルードされた別のサーブレット内)で、PrintWriter
とServletOutputStream
を両方とも使用できます。どちらの出力も、同じバッファに書き込まれます。
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
インタフェースの関連メソッドを使用して、レスポンス・バッファのサイズを操作することができます。詳細は、http://www.oracle.com/technetwork/java/javaee/servlet/index.html#specs
のServlet 2.4仕様を参照してください。
WebLogic Serverのレスポンス・バッファのデフォルト値は12Kで、バッファ・サイズは、CHUNK_SIZE
(CHUNK_SIZE = 4088
バイト)に基づいて内部で計算されます。たとえば、ユーザーが5KBに設定すると、バッファ・サイズは、それを切り上げた最も近い
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サイトが損なわれるおそれがあります。詳細は、「サーブレットでのクライアント入力のセキュリティ」を参照してください。 |
この項では、リクエスト・オブジェクトからデータを取得できる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
がスローされます。
例9-1では、より個人的な挨拶文を表示するために、問合せパラメータとしてユーザー名を受け付けるように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; } // 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のセキュリティ勧告)を参照してください。
セキュリティの脆弱性をなくすには、ユーザーが入力したデータを戻す前に、そのデータをスキャンして、表9-1に示すHTML特殊文字があるかどうかを調べます。特殊文字が見つかった場合、HTMLのエンティティ参照または文字参照と置き換えます。文字を置換することで、ブラウザでユーザー入力によるデータがHTMLとして実行されることを回避します。
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-2ではこのメソッドを使用すべきかどうかを検討すべきその他の場所を示します。
Cookieは、クライアントからサーバーに戻されるので、そのクライアントを識別するために便利なものです。ブラウザは、同じサーバーにアクセスするたび、HTTPリクエストと共にそのサーバーに関連したCookieをすべて送ります。Cookieは、クライアントからサーバーに戻されるので、そのクライアントを識別するために便利なものです。
各Cookieは名前と値を保持しています。通常、Cookieをサポートしているブラウザでは、各サーバーのドメインに、1つあたり最大4KのCookieを20まで格納することができます。
ブラウザ上に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にユーザーの詳細情報を格納して、後で参照できるようにします。
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リクエストは異なるポートに送られるので、ブラウザによっては、HTTPリクエストに入れて送られてきたCookieを、その後続のHTTPSリクエストに包含しない(あるいはその逆)ことがあります。このような場合には、サーブレット・リクエストがHTTPとHTTPSの間で切り替わると、新しいセッションが作成されることになります。セッション内でリクエストが行われるたびに、特定のドメインによって設定されるすべてのCookieがサーバーに送られるようにするには、cookie-domain
要素をドメイン名に設定します。cookie-domain
要素は、WebLogic固有のデプロイメント記述子weblogic.xml
のsession-descriptor
要素の下位要素です。例:
<session-descriptor> <cookie-domain>mydomain.com</cookie-domain> </session-descriptor>
cookie-domain
要素は、mydomain.com
によって指定されたドメイン内のホストに、すべてのリクエストについて適切な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サーブレットを記述する際には、JNDI、JMS、EJB、JDBC接続など、WebLogic Serverの豊富な機能を利用できます。
以下のドキュメントには、これらの機能の詳細が記載されています。
Oracle WebLogic Server WebLogic Enterprise JavaBeansプログラミング
Oracle WebLogic Server JDBCのプログラミング
Oracle WebLogic Server JNDIのプログラミング
Oracle WebLogic Server JMSのプログラミング
WebLogic Serverは、サーブレットを含むサーバー側JavaクラスからのJava Database Connectivity (JDBC)の使用をサポートしています。JDBCを使用すると、JavaクラスからSQLクエリを実行し、クエリの結果を処理できます。JDBCおよびWebLogic Serverの詳細は、『Oracle WebLogic Server JDBCのプログラミング』を参照してください。
次の項で説明するように、JDBCはサーブレットで使用できます。
DataSource
は、接続プールを参照するサーバー側オブジェクトです。接続プールの登録により、JDBCドライバ、データベース、ログインなど、データベース接続と関連するパラメータが定義されます。DataSource
オブジェクトおよび接続プールは、管理コンソールで作成します。
ヒント: Java EE準拠のアプリケーションを作成する場合は、DataSource オブジェクトの使用をお薦めします。 |
管理コンソールを使用して接続プールを登録します。詳細は、Oracle WebLogic Server管理コンソール・ヘルプのJDBCデータ・ソース: 構成: 接続プールに関する項を参照してください。
接続プールを指すDataSource
オブジェクトを登録します。
JNDIツリーで、DataSource
オブジェクトをルックアップします。例:
Context ctx = null; // Get a context for the JNDI look up ctx = new InitialContext(ht); // Look up the DataSource object javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup ("myDataSource");
DataSource
を使用して、JDBC接続を作成します。例:
java.sql.Connection conn = ds.getConnection();
接続を使用して、SQL文を実行します。例:
Statement stmt = conn.createStatement(); stmt.execute("select * from emp"); . . .
データベースへの直接接続は、データベース接続を確立する方法としては、最も効率の悪いものです。リクエストごとに新しいデータベース接続を確立しなければならないからです。データベースへの接続には、どのJDBCドライバも使用できます。Oracleでは、OracleおよびMicrosoft SQL Server用のJDBCドライバを提供しています。詳細は、『Oracle WebLogic Server JDBCのプログラミング』を参照してください。
サーブレットの設計時に、高い負荷のもとで、WebLogic Serverがサーブレットをどのように呼び出すか検討する必要があります。複数のクライアントが同時にサーブレットをヒットすることは避けられません。したがって、サーブレットのコードは、共有リソースやインスタンス変数の共有違反を防ぐように記述します。
共有リソースの問題は、個々のサーブレットごとに処理することをお薦めします。次のガイドラインを念頭に置いてください。
可能な限り、同期を避けます。引続き発生するサーブレットのリクエストが、現行のスレッドが完了するまで、ボトルネックになるためです。
各サーブレット・リクエストに固有の変数は、サービス・メソッドのスコープ内で定義します。ローカル・スコープ変数は、スタックに保存されるため、同一のメソッド内で実行されている複数のスレッドで共有されることはありません。したがって、同期の必要性はなくなります。
外部リソースへのアクセスは、Classレベルで同期を取るか、トランザクションにカプセル化します。
この項では、リクエストをサーブレットから別のリソースへディスパッチするのによく使用されるメソッドの概要を説明します。
サーブレットでは、リクエストを別のリソース(サーブレット、JSP、またはHTMLページなど)に渡すことができます。このプロセスは、リクエストのディスパッチと呼ばれます。リクエストをディスパッチする場合は、RequestDispatcher
インタフェースのinclude()
メソッドまたはforward()
メソッドを使用します。
リクエストのディスパッチに関する詳細な説明については、Servlet 2.4仕様(http://www.oracle.com/technetwork/java/javaee/servlet/index.html#specs
を参照)の8.2項を参照してください。
RequestDispatcher
を使用すると、HTTPリダイレクト・レスポンスをクライアントに送り返す必要がなくなります。RequestDispatcher
は、リクエストされたリソースにHTTPリクエストを渡します。
リクエストを特定のリソースにディスパッチするには:
次のように、ServletContext
への参照を取得します。
ServletContext sc = getServletConfig().getServletContext();
以下のメソッドの1つを用いて、RequestDispatcher
オブジェクトをルックアップします。
RequestDispatcher rd = sc.getRequestDispatcher(String
path
);
path
は、Webアプリケーションのルートに対する相対パスである必要があります。
RequestDispatcher rd = sc.getNamedDispatcher(String name)
;
name
を、Java EE標準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
オブジェクトを使用して、リクエストを別のサーブレットに転送します。
適切なメソッドを使用して、リクエストを転送またはインクルードします。
rd.forward(request,response);
(「リクエストの転送」を参照)
rd.include(request,response);
(「リクエストのインクルード」を参照)
一度、正しい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リソースの「サーバー側インクルード」を実現できます。
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.xml
でfilter-dispatched-requests-enabled
要素を設定することで、変更できます。この要素は、ディスパッチされた(転送またはインクルード)リクエストにフィルタを適用するかどうかを制御するものです。古いDTDベースのデプロイメント記述子のデフォルト値はtrue
です。新しいスキーマ・ベースの記述子のデフォルトはfalse
です。
RequestDispatcherとフィルタの詳細は、http://www.oracle.com/technetwork/java/javaee/servlet/index.html#specs
のServlet 2.4仕様の6.2.5項を参照してください。WebLogic Server用のフィルタの記述と構成の詳細は、第14章「フィルタ」を参照してください。
次の項では、別のWebサーバーにHTTPリクエストをプロキシする方法について説明します。
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アプリケーションをデプロイします。
セカンダリHTTPサーバーのプロキシを設定するには:
プロキシ
・サーブレットをWebアプリケーション・デプロイメント記述子に登録します(「ProxyServletとともに使用するweb.xmlのサンプル」を参照)。
Webアプリケーションは、リクエストに応答するサーバー・インスタンスのデフォルトWebアプリケーションである必要があります。プロキシ・サーブレットのクラス名は、weblogic.servlet.proxy.HttpProxyServlet
です。
<param-name>
にredirectURL
を指定し、<param-value>
にプロキシされるリクエストのリダイレクト先サーバーのURLを指定して、ProxyServlet
の初期化パラメータを定義します。
独自の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 WebLogic Serverコマンド・リファレンス』のWebLogic Server Javaユーティリティの使用に関する項を参照してください。
ProxyServlet
を<url-pattern>
にマップします。特に、プロキシするファイルの拡張子(*.jsp
、*.html
など)をマップします。Webアプリケーション・デプロイメント記述子web.xml
で<servlet-mapping>
要素を使用します。
<url-pattern>
を「/」に設定した場合、WebLogic Serverによって解決できないリクエストはすべてリモート・サーバーにプロキシされます。しかし、拡張子が*.jsp
、*.htm
、および*.html
のファイルをプロキシする場合、これらの拡張子もマップする必要があります。
受信するリクエストをリダイレクトする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 WebLogic Serverクラスタの使用方法』のサーブレットとJSPのレプリケーションとフェイルオーバーに関する項を参照してください。
注意: サーブレットの自動フェイルオーバーでは、サーブレット・セッション状態をメモリー内にレプリケートする必要があります。手順は、『Oracle WebLogic Serverクラスタの使用方法』のインメモリーHTTPレプリケーションの構成に関する項を参照してください。 |
サーブレットに関してWebLogic Serverクラスタがサポートしているロード・バランシングと、関連するプランニングと構成に関する開発者および管理者向け考慮事項の詳細は、『Oracle WebLogic Serverクラスタの使用方法』のサーブレットとJSPのロード・バランシングに関する項を参照してください。
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サーブレットで読取り可能です。
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
ユーティリティは、Java EE固有のユーティリティではありません。このユーティリティは、weblogic.xml
デプロイメント記述子ファイルで構成します。このユーティリティを使用すると、ユーザーは、web.xml
デプロイメント記述子に指定されたデフォルトのURLパターン・マッチングではなく、Apacheスタイルのパターン・マッチングを指定することができます。詳細については、「url-match-map」を参照してください。
一般的に、WebLogic Serverが着信するHTTPリクエストを処理すると、レスポンスは即座にクライアントに返されます。そのような接続は、同じスレッドによって同期的に処理されます。しかし、一部のHTTPリクエストでは、より長い処理時間が必要となることがあります。たとえばデータベース接続で生じるレスポンス時間は、より長くなる場合があります。これらのリクエストの同期的な処理では、そのリクエストが処理されてレスポンスが送信されるまで待機が行われ、スレッドが保持されます。
このようにスレッドがハングしてしまう状況を回避するため、WebLogic Serverでは、着信するリクエストを処理するスレッドからレスポンスを切り離すことによって、HTTPリクエストを非同期に処理する、2つのクラスが用意されています。次の項で、これらのクラスについて説明します。
抽象非同期サーブレットを使用すると、着信するリクエストとサーブレット・レスポンスを、別々のスレッドで処理できます。このクラスはスレッド処理を含めて、将来的レスポンス・サーブレットよりもレスポンスを処理するための全般的なフレームワークが優れています。
抽象非同期サーブレットを実装するには、weblogic.servlet.http.AbstractAsyncServlet.java
クラスを拡張します。このクラスにより、拡張クラスでオーバーライドする必要のある以下の抽象メソッドが提供されます。
このメソッドは、サーブレット・リクエストを処理します。次のサンプル・コードでは、このメソッドをオーバーライドする方法を示します。
例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; } }
このメソッドは、サーブレット・レスポンスを処理します。
注意: 元の着信リクエスト・メソッドの処理に使用される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!"); }
このメソッドは、タイムアウト期間中に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(); } } }