フィルタの追加と削除

Web アプリケーションに新規フィルタを追加するには、フィルタを定義し、リソースにマッピングする必要があります。これは、Web アプリケーションのデプロイメントディスクリプタ (web.xml ファイル) を使用して行います。このセクションでは、フィルタを定義し、その実行順番を指定するためのオプションについて説明します。

フィルタは web.xml ファイル内で定義します。したがって、Web アプリケーションを再コンパイルせずに、フィルタの順番の変更、初期化パラメータの定義、フィルタの追加および削除を行うことができます。この疎結合アーキテクチャにより、フィルタの設定タスクとアプリケーションの記述タスクを分離できます。アプリケーションとその基盤リソースは、リクエストとレスポンスを処理するフィルタとは別個に動作します。

web.xml ファイル内では、フィルタの他の設定情報を定義できます。詳細については、 「初期化パラメータへのアクセス」 を参照してください。

フィルタの定義

web.xml ファイルではまず、web-app ブロック内に名前とクラスによってフィルタを定義します。次のサンプルは web.xml ファイルのフィルタ定義部分です。

<web-app>
...
<filter>
 <filter-name>filter_name</filter-name>
 <filter-class>filter_class</filter-class>
</filter>
...
</web-app>

たとえば、TimingFilter を定義するには、次のコードを使用します。

<filter>
 <filter-name>TimingFilter</filter-name>
 <filter-class>jrunsamples.filters.TimingFilter</filter-class> 
</filter>

また、フィルタブロック内に初期化パラメータを定義することもできます。詳細については、 「初期化パラメータへのアクセス」 を参照してください。

フィルタのマッピング

フィルタをマッピングするリソースを定義するには、url-pattern 要素または
servlet-name 要素を使用します。さまざまなフィルタ名を使用して同じフィルタを複数回宣言できます。そのため、URL マッピングとサーブレット名を組み合わせることによって 1 つのフィルタを複数のリソースにマッピングできます。

URL パターンへのフィルタのマッピング

web.xml ファイル内で URL パターンを使用してフィルタを定義できます。シンタックスは次のとおりです。

<web-app>
...
<filter>
 <filter-name>filter_name</filter-name>
 <filter-class>filter_class</filter-class>
</filter>
...
<filter-mapping> 
 <filter-name>filter_name</filter-name>
 <url-pattern>pattern_for_resource</url-pattern>
</filter-mapping>
...
</web-app>

たとえば、TimingFilter を welcome.jsp ファイルにマッピングするには、次のコードを使用します。

<filter>
 <filter-name>TimingFilter</filter-name>
 <filter-class>jrunsamples.filters.TimingFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>TimingFilter</filter-name>
 <url-pattern>/welcome.jsp</url-pattern>
</filter-mapping>

url-pattern は、web.xml ファイル内でサーブレットをリソースにマッピングする際に使用する要素と同じなので注意してください。

URL マッピングを web.xml ファイル内で使用した場合と同様に、url-pattern 要素内でワイルドカードを使用することによって複数のリソースにフィルタをマッピングできます。たとえば、Web アプリケーションのコンテキストにおいて、フィルタをすべての JSP にマッピングするには、パターンを次のように定義します。

<url-pattern>/*.jsp</url-pattern>

サーブレット、JSP、およびスタティックコンテンツすべてにフィルタをマッピングするには、パターンを次のように定義します。

<url-pattern>/*</url-pattern>

指定したサーブレットへのフィルタのマッピング

フィルタは、指定したサーブレットにマッピングすることもできます。次のシンタックスは、フィルタを指定したサーブレットにマッピングします。

<web-app>
...
<servlet>
 <servlet-name>servlet_name</servlet-name>
 <servlet-class>servlet_class</servlet-class>
</servlet>
...
<filter>
 <filter-name>filter_name</filter-name>
 <filter-class>filter_class</filter-class>
</filter>
...
<filter-mapping>
 <filter-name>filter_name</filter-name>
 <servlet-name>servlet_name</servlet-name>
</filter-mapping>
...
</web-app>

チェーン内のフィルタの順番指定

JRun は、リクエスト URI が web.xml ファイル内で一致する URL パターンとサーブレットに従って、各リクエストのフィルタの実行順番を指定します。JRun はまず、url-pattern の一致候補を検証します。web.xml ファイル内ではこの順番で url-pattern の一致候補が並んでいます。url-pattern の後には servlet-name の一致候補が続きます。web.xml ファイル内ではこの順番で servlet-name の一致候補が並んでいます。

次のサンプルでは、フィルタ 1 およびフィルタ 3 は URL マッピングを使用してリソースにマッピングされ、フィルタ 2 はサーブレット名マッピングを使用します。したがって、JRun は、フィルタ 1、フィルタ 3、フィルタ 2 の順番でサーブレットを呼び出します。

<web-app>
...
<filter>
 //フィルタ 1 の定義
</filter>
<filter>
 //フィルタ 2 の定義
</filter>
<filter>
 //フィルタ 3 の定義
</filter>
<filter-mapping>
 //フィルタ 1 のマッピング
 <url-mapping>...</url-mapping>
</filter-mapping>
<filter-mapping>
 //フィルタ 2 のマッピング
 <servlet-name>...</servlet-name>
</filter-mapping>
<filter-mapping>
 //フィルタ 3 のマッピング
 <url-mapping>...</url-mapping>
</filter-mapping>
...
</web-app>

初期化パラメータへのアクセス

フィルタは、Web アプリケーションの web.xml ファイルから初期化パラメータを抽出できます。これにより、Web アプリケーションを再コンパイルせずに、処理中に設定値を変更できます。たとえば、データソースまたはログファイルのロケーションは、ハードコードではなく初期化パラメータで設定することができます。

フィルタ定義内で初期化パラメータを使用しても、サーブレットの場合と同様に動作します。FilterConfig クラスは、ServletConfig クラスと同じ名前を共有するメソッドを使用してこれらのパラメータにアクセスします。

getInitParamter()
getInitParameterNames()

web.xml ファイル内で初期化パラメータの名前と値を定義するには、init-param 要素を使用します。シンタックスは次のとおりです。

...
<filter>
 <filter-name>filter_name</filter-name>
 <filter-class>filter_class</filter-class>
 <init-param> 
  <param-name>parameter_name</param-name>
  <param-value>parameter_value</param-value> 
 </init-param>
</filter>
...

フィルタごとに任意の数の初期化パラメータを指定できます。たとえば、次のように指定できます。

<filter>
 <filter-name>InitParamsFilter</filter-name>
 <filter-class>jrunsamples.filters.InitParamsFilter</filter-class>
 <init-param> 
  <param-name>message</param-name>
  <param-value>Drink your Ovaltine.</param-value> 
 </init-param> 
 <init-param> 
  <param-name>answer</param-name>
  <param-value>42</param-value> 
 </init-param> 
</filter>

フィルタの doFilter メソッドの次のコードサンプルは、初期化パラメータのリストを取得し、標準出力でリストをプリントします。

...
Enumeration initParams = filterConfig.getInitParameterNames();
if (initParams == null) {
 System.out.println("フィルタ定義内に初期化パラメータがありません");
 chain.doFilter(request, response);
} else {
 while (initParams.hasMoreElements()) {
 String name = (String) initParams.nextElement();
 String value = filterConfig.getInitParameter(name);
 System.out.println(name + ":"+ value);
  }
}
...

簡単なフィルタサンプル

簡単なフィルタサンプルとしては TimingFilter があります。このフィルタは、JRun がリクエストの処理を開始する時刻をミリ秒単位で表示します。リクエストがチェーンから戻ると、JRun は再び時刻を表示します。

このフィルタは、doInit および doDestroy メソッドがオーバーライドされないように、GenericFilter インターフェイスを拡張します。

package jrunsamples.filters;
import javax.servlet.*;
import javax.servlet.http.*;
public class TimingFilter extends GenericFilter {
 public void doFilter(ServletRequest request, ServletResponse 
response, FilterChain chain) throws java.io.IOException, 
javax.servlet.ServletException {
  long bef = System.currentTimeMillis();
  System.out.println("フィルタの開始:" + bef);
  chain.doFilter(request,response);
  long aft = System.currentTimeMillis();
  System.out.println("フィルタの終了:" + aft);
 }
}

Web アプリケーションの web.xml デプロイメントディスクリプタで、フィルタおよびマッピングは次のように定義されています。

<filter> 
 <filter-name>TimingFilter</filter-name>
 <filter-class>jrunsamples.filters.TimingFilter</filter-class> 
</filter>
<filter-mapping> 
 <filter-name>TimingFilter</filter-name>
 <url-pattern>/*</url-pattern> 
</filter-mapping> 

このアプリケーションコンテキスト内でリソースを呼び出すと、次のようなメッセージがシステムコンソールにプリントされます。

11/20 14:42:47 info JSPServlet:init
フィルタの開始: 1006285367836
フィルタの終了: 1006285367956

ラッパーの使用

フィルタは、ServletContext を使用してリクエストおよびレスポンスオブジェクトのヘッダーを表示できます。また、フィルタは、このオブジェクトの属性にアクセスすることもできます。ただし、リクエストまたはレスポンスの実際のコンテンツを変更するには、フィルタは、リクエストまたはレスポンスのコンテンツストリームをクライアントに戻される前に阻止できなければなりません。これは、次のラッパークラスを使用して実行します。

javax.servlet.http.ServletRequestWrapper(ServletRequest req)
javax.servlet.http.HttpServletRequestWrapper(HttpServletRequest req)
javax.servlet.http.ServletResponseWrapper(ServletResponse resp)
javax.servlet.http.HttpServletResponseWrapper(HttpServletResponse 
resp)

デフォルトでは、これらのラッパークラスは、すべてのメソッド呼び出しを、メソッドの取り出し元であるクラスに変更せずに渡します。ラッパーを使用すると、ラップされたオブジェクトが公開するすべてのメソッドをオーバーライドすることができます。

デフォルトでは、HttpServletRequestWrapper クラスは、すべてのメソッド呼び出しを、このクラスがラップしている HttpServletRequest オブジェクトに渡します。リクエストメソッド (isUserInRolegetRemoteUser など) をオーバーライドするには、ラッパーを作成し、それらのメソッドをラッパー内に実装し、ラッパー上でこれらのオーバーライドするメソッドを呼び出します。

デフォルトでは、HttpServletResponseWrapper クラスは、このクラスがラップしている HttpServletResponse オブジェクトにすべてのメソッド呼び出しを渡します。レスポンスメソッド (getWritergetLocale など) をオーバーライドするには、ラッパー上のこれらのオーバーライドされたメソッドを呼び出します。

リクエストやレスポンスがラップされているかどうかにかかわらず、チェーン内の他のフィルタは、このオブジェクトを、他のリクエストまたはレスポンスと同様に処理します。チェーン内の他のフィルタは、オブジェクトがラップされているかどうかを判断することはできません。

オブジェクトがラッパーであるかどうかを判断するには、instanceOf 演算子を使用してください。