| Oracle® Fusion Middleware Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発 12c リリース1 (12.1.1) B65890-02 |
|
![]() 前 |
![]() 次 |
この章では、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インタフェースの関連メソッドを使用して、レスポンス・バッファのサイズを操作することができます。詳細は、サーブレット3.0仕様(http://jcp.org/en/jsr/detail?id=315)を参照してください。
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 Enterprise JavaBeansバージョン2.1のプログラミング
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準拠のアプリケーションを作成する場合は、 |
管理コンソールを使用して接続プールを登録します。詳細は、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()メソッドを使用します。
リクエストのディスパッチの詳細は、サーブレット3.0仕様(http://jcp.org/en/jsr/detail?id=315)の9.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リソースの「サーバー側インクルード」を実現できます。
サーブレット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.0仕様(http://jcp.org/en/jsr/detail?id=315)を参照してください。WebLogic Server用のフィルタの記述と構成の詳細は、「フィルタ」を参照してください。
次の項では、別の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サーバーのプロキシを設定するには:
proxyサーブレットをWebアプリケーション・デプロイメント記述子に登録します(例9-2を参照)。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 Server抽象非同期サーブレットの代わりに、サーブレット3.0仕様で定義されている標準非同期処理モデルを使用することをお薦めします。 |
抽象非同期サーブレットを使用すると、着信するリクエストとサーブレット・レスポンスを、別々のスレッドで処理できます。このクラスはスレッド処理を含めて、将来的レスポンス・サーブレットよりもレスポンスを処理するための全般的なフレームワークが優れています。
抽象非同期サーブレットを実装するには、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;
}
}
このメソッドは、サーブレット・レスポンスを処理します。
|
注意: 元の着信リクエスト・メソッドの処理に使用される |
処理中に例外が発生した場合、コンテナはクライアントにエラーを返します。次のサンプル・コードでは、このメソッドをオーバーライドする方法を示します。
例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()メソッドが呼び出されなかった場合に、サーブレット・レスポンス・エラーを送信します。
|
注意: 元の着信リクエスト・メソッドの処理に使用される |
例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!");
}
|
注意: このリリース以降は、サーブレット3.0仕様で定義されている標準非同期処理モデルを使用することをお薦めします。 |
また、将来的レスポンス・サーブレットを使用して、受信リクエストを処理するスレッドとは別のスレッドでサーブレット・レスポンスを処理することもできます。このサーブレットを有効化するには、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();
}
}
}