- PR -

Filter内でのレスポンスへの書き込みがうまくできません。

1
投稿者投稿内容
どらぴ
常連さん
会議室デビュー日: 2003/10/06
投稿数: 40
投稿日時: 2004-03-03 14:06
質問させてください。

ServletFilterを使用してWebアプリケーションの出力に手を加えようと思っています。

ServletFilterのdoFilterメソッド内のjavax.servlet.FilterChain.doFilterメソッドの
前にはWebアプリケーションに渡す前に行う処理、
後ろにWebアプリケーションに渡した結果に手を加える処理
を記述すると理解しています。

以下がサンプルです。
とくに何も行いません。

ServletFilterのdoFilterメソッドの中身

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {

try {
final ByteArrayOutputStream dummyout = new ByteArrayOutputStream();

   // 仮の出力先を持つServletResponseWrapperを定義
HttpServletResponseWrapper wrapper =
   new HttpServletResponseWrapper((HttpServletResponse) res) {

PrintWriter writer = new PrintWriter(dummyout);
ServletOutputStream out = new DummyServletStream(dummyout);

public PrintWriter getWriter() {
return writer;
}

public ServletOutputStream getOutputStream() {
return out;
}
};

// 仮の出力先にWebアプリケーションの出力を書き込ませる
chain.doFilter(req, wrapper);

bais.close();
baos.close();

// 仮の出力先のコンテンツを本来の出力先へ出力
String content = new String(baos.toByteArray(),"UTF8");

res.setContentLength(content.getBytes(ENCODING).length); ---@
res.setContentType("text/html;charset="+ENCODING); ---A

PrintWriter reswriter = res.getWriter();
reswriter.write(content);
reswriter.close();

} catch (IOException ex) {
} catch (ServletException ex) {
}
}

これを実行すると@、Aのところで
『WARNING: Cannot set header. Response already committed.』
と怒られてしまいます。

javax.servlet.FilterChain.doFilterの前に@、Aの処理以外にsetHeaderなどを行うと『WARNING』は発生しません。
また直後に元のレスポンスresを参照して
res.isCommited() --->レスポンスがコミットされたかどうかを論理値で返す。
とするとtrueが返され、元のレスポンスがコミットされているようです。

どうにかしてjavax.servlet.FilterChain.doFilterの後にレスポンスに手を加えたいのですが、どうしたらよいでしょうか?

どなたでもご存知の方いらっしゃいましたらご教示よろしくお願いします。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-03-03 15:41
getWriter と getOutputStream 両方を上書きしていないときによく見かけるエラーですね・・・。どらぴさんのコードでは該当しませんが。
サーブレットコンテナには何をお使いでしょう?
WebLogicだと requestDispatcher.forward() でもフィルタが動きますので、2重にフィルタされていないかチェックする必要があり、そのような現象が発生するかのうせいがあります。フラグは request に setAttribute() でもして判定すれば良いでしょう。
どらぴ
常連さん
会議室デビュー日: 2003/10/06
投稿数: 40
投稿日時: 2004-03-05 10:06
インギさん、返答ありがとうございます。
サーブレットコンテナはWebSphereですね。
引用:

2重にフィルタされていないかチェックする必要があり、そのような現象が発生するかのうせいがあります。


2重にFilterがあるとこのような現象が発生するのでしょうか?
その場合はどう対処すれば良いのでしょう?
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-03-05 11:26
WASならフィルタが2重に起動されることはないと思います。

WebLogicでチェックをかけるとしたら
[CODE]
doFilter(...){
if(null != request.getAttribute("flag")){
request.setAttribute("flag","kicked");
//処理
}
chain.doFilter(..);
}
{/CODE]

どのコンテンツが本物のレスポンスに書き出されているかを切り分けるために、 chain.doFilter() の直後に flush() してみてはいかがでしょうか?
どらぴ
常連さん
会議室デビュー日: 2003/10/06
投稿数: 40
投稿日時: 2004-03-05 14:38
引用:

WASならフィルタが2重に起動されることはないと思います。


最初意味があまりわからなかったのですが

インギさんの「2重」というのは

  • 「同じFilter」が2重に働く

ということですよね?
私の「2重のFilter」というのは

  • 「違うFilter」が2つ以上働く

というように勝手に思い込んでしまっていました。

インギさんの書き込み参照
引用:

2重にフィルタされていないか


私の書き込み参照
引用:

2重にFilterがあると



ラッパーしたもの、オリジナルのレスポンスどちらの
OutputStreamもflushすると「Writerを既に取得した」というエラーが発生し、
Writerをflushすると特に何も起こりませんでした。
しかしその後setHeaderを行うと同様に『WARNING』が発生しました。

…?です。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-03-05 15:27
説明が悪くてごめんなさい。確かにわかりにくかったですね。

さて、現象のほうですがちょっとわかりません。
WAS には明るくないのですが、サンプルにレスポンスをラッパで受けてから返すようなフィルタはついていないでしょうか?それとみくらべてみたり、IBM に問い合わせてみたりしてみてはいかがでしょう。せっかく商用の製品を使っているんですから。
1

スキルアップ/キャリアアップ(JOB@IT)