Intercepting Filter パターン

このセクションでは、 「パターンテンプレート」 で定義されている形式の Intercepting Filter デザインパターンについて説明します。

問題

状況により、リクエストとレスポンスの前処理および後処理が必要になる場合があります。次の疑問点を確認してください。

Web アプリケーションには、設定ファイルで定義されている、ネストされた if/else ステートメントまたは URL パターンマッチングよりも柔軟で再利用性が高い手法が必要になります。その理由は次のとおりです。

解決策

Intercepting Filter パターンは、次のような一般的なサービスを処理する埋め込み可能なフィルタのロールを定義します。

フィルタは、受信リクエストと送信レスポンスを阻止します。フィルタの再設定は、Web アプリケーションの web.xml デプロイメントディスクリプタを使用して行うことができます。そのため、フィルタを追加および削除する際にアプリケーションを再コンパイルする必要はありません。また、フィルタの順番を変更したり、フィルタの動作をダイナミックに変更することができます。

フィルタを使用したサーブレットの詳細については、弟 7 章、「フィルタ」を参照してください。

戦略

フィルタをサーブレットとして実装するには、javax.servlet.Filter インターフェイスを使用します。

フィルタを使用すると、チェーン内の他のフィルタにリクエストおよびレスポンスを転送したり、別のサーブレットとして実装されているフィルタコントローラにリクエストやレスポンスを渡したりすることができます。

フィルタを実装する方法としては、デコレータパターンまたは非デコレータパターンがあります。これらのマイクロパターンについては、次のセクションで説明します。

デコレータマイクロパターン

デコレータマイクロパターンは、新規機能を個々のオブジェクトにダイナミックかつ透過的に追加するクラスで構成されています。これらの機能は、他のフィルタの形態をとることがあり、あるフィルタから次のフィルタへの制御の流れをコントロールすることによって、フィルタをダイナミックに埋め込み可能にすることができます。

次の図にこの戦略を示します。

これは、クライアントが、まずコントローラに渡された後にデバッグフィルタ、認証フィルタ、最後にコア処理コンポーネントに渡されるリクエストを行っているイメージを示しています。

このフィルタは他のコンポーネントを使用してリクエストパラメータを設定します。

Decorator Filter のサンプル

次のサンプルは、基本的なフィルタに埋め込まれたフロー制御ロジックを示しています。承認されていないユーザーがターゲットリソースにアクセスしようとすると、フィルタは、2 つのリクエスト属性を設定した後にそのリクエストをエラーページに転送します。ユーザーにターゲットリソースへのアクセスが承認されている場合、フィルタは、チェーン内の次のフィルタにリクエストを渡します。実際には、チェーン内の各フィルタは、リクエストおよびレスポンスオブジェクトをデコレートするか、ラップします。

...
public class AuthFilter extends GenericFilter {
 public void doFilter(ServletRequest request, ServletResponse 
response, FilterChain chain) throws IOException, 
ServletException {
  System.out.println("AuthFilter の開始");
  HttpServletRequest req = (HttpServletRequest)request;
  String authorization = req.getHeader("Authorization");
  if (authorization != null) {
   System.out.println("承認されたユーザー:" + req.getRemoteUser() + "承認
されました");
   chain.doFilter(request,response);
  } else {
   System.out.println("承認されなかったユーザー:" + req.getRemoteUser()  + 
" 拒否されました");
   request.setAttribute("unauth_user", req.getRemoteUser());
   request.setAttribute("req_resource", req.getRequestURI());
   RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
   rd.forward(request, response);
  }
  System.out.println("AuthFilter の終了");
 }
}

非デコレータマイクロパターン

フィルタオブジェクトは、非デコレータとして、または、その基本スコープ外には他に仕事を持たないオブジェクトとして実装できます。この場合、フィルタは、フロー制御ロジックを持たず、フィルタの機能だけを実装します。フィルタマネージャは、フィルタチェーンの集中アクセスポイントとして機能し、その独自のフロー制御ロジックセットに基づいてリクエストを転送します。

次の図にこの戦略を示します。

コントローラは、フィルタマネージャにリクエストを渡します。フィルタマネージャは、デバッグフィルタ、認証フィルタ、コア処理コンポーネントの順にリクエストを渡します。

非Decorator Filter のサンプル

次のコードは、ロギング機能を提供する非 Decorator Filter を示しています。アプリケーションを再コンパイルせずに、このフィルタをフィルタチェーンに追加したり、フィルタチェーンから削除したりすることができます。このフィルタは、標準 J2EE コンポーネントを使用するため、任意の Web アプリケーションに埋め込むことができます。

package jrunsamples.filters;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class HeaderFilter extends GenericFilter {
 public void doFilter(ServletRequest req, ServletResponse resp, 
FilterChain chain) throws java.io.IOException, 
javax.servlet.ServletException {
  chain.doFilter(req, resp);
  HttpServletRequest request = (HttpServletRequest)req;
  System.out.println("******リクエストヘッダー******");
  System.out.println("リクエスト:" + request.getRequestURI());
  Enumeration headers = request.getHeaderNames();
  if (headers == null) {
  } else {
   while (headers.hasMoreElements()) {
    String name = (String) headers.nextElement();
    String value = request.getHeader(name);
    System.out.println(name + "=" + value);
   }
  }
  HttpServletResponse response = (HttpServletResponse)resp;
  System.out.println("******レスポンス情報******");
  String charencode = response.getCharacterEncoding();
  int bufsize = response.getBufferSize();
  System.out.println("文字エンコード:" + charencode);
  System.out.println("バッファーサイズ:" + bufsize);
 }
}