HttpServletRequestWrapper にてはまる
ServletAPI に HttpServletRequestWrapper という、HttpServletRequest インタフェースを実装したラッパークラス(の元になるクラス)がありますよね?
私は、長らくアレはただの便利クラスだと思っていました。
・・・が、どうやらAPIとして用意されていることに意味はあったようです。
サーブレットコンテナに Tomcat を使用している場合、HttpServletRequest をラップして何らかの処理を追加したい場合等は、必ず HttpServletRequestWrapper を継承する必要があるようです・・・ *1 *2
Tomcat では、フォワード時に、HttpServletRequestWrapper の getRequest メソッド、setRequest メソッドを使用して、ラップされている HttpServletRequest の内側にある、Tomcat が生成したインスタンスを直接入れ替えているようです。
そのため、フォワード時に、HttpServletRequestWrapper のインスタンスではない、HttpServletRequest 実装クラスを RequestDispatcher の forward メソッドに渡すと、ClassCastException が発生します。
forward 時のメソッド引数は ServletRequest 型であり、ServletRequestWrapper 型ではありません。にもかかわらず、サーブレットコンテナが Tomcat の場合、引数に渡したオブジェクトが ServletRequestWrapper 型であることを強要します。非常に納得いきません。
何のためのインタフェースなのかと・・・・orz
ServletAPI の Javadoc を見ても、リクエストを拡張する場合はServletRequestWrapper/HttpServletRequestWrapper を使用しなければならないとは記述されていません。なので、この動作は Tomcat 特有のものなのかもしれません。
ちなみに、下記のコードがうまく動作せずに、嵌りました(汗
public class XXXXHttpServletRequest extends HttpServletRequestWrapper { private String uri; public XXXXHttpServletRequest(HttpServletRequest request, String uri) { super(request); this.uri = uri; } public String getRequestURI() { return uri; } }
リクエストの URI を偽装するサーブレットリクエストを作成したところ、フォワードされた先でも、URI が偽装された物になってました。
これは、Tomcat では forward が行なわれた時に、ServletRequestWrapper の持つ getRequest メソッドと setRequest メソッドを使用して、Tomcat 自体が生成したリクエストオブジェクトを入れ替えることで、リクエスト URI などをフォワード先のものになるようにしているためです。
上記のクラスでは、ラップしたリクエストの getRequestURI メソッドがどんな値を返しても、常にコンストラクタで指定された URI を返すようにしてしまっているため、フォワード先で、リクエスト URI を取得した場合に、期待したのと異なる値が取得されてしまいます。
そこで、Tomcat がフォワード時に、ServletRequestWrapper の getRequest メソッド、setRequest メソッドを使用しないように、HttpServletRequestWrapper は使用せずに、HttpServletRequest インタフェースのラッパークラスを作成して試してみたところ、上で書いた ClassCastException が発生したということです。
で、結局、泥臭く下記のような記述をしてしまいました・・・微妙。
public class XXXXHttpServletRequest extends HttpServletRequestWrapper { private String uri; private String originalURI; public XXXXHttpServletRequest(HttpServletRequest request, String uri) { super(request); this.uri = uri; this.originalURI = request.getRequestURI(); } public String getRequestURI() { if (originalURI.equals(super.getRequestURI())) return uri; else return super.getRequestURI(); }