You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Michael Ludwig <mi...@gmx.de> on 2008/11/12 02:37:29 UTC

[Http]ServletResponseWrapper.getOutputStream()

This is in Tomcat 6.0.18 on Java 1.6.

A servlet S includes an HTML fragment via RequestDispatcher.include()
while being wrapped by a filter F and being passed a wrapped response
WR.

Servlet S generates header and footer using the PrintWriter.

The HTML fragment, being a static resource, is served by the Tomcat's
DefaultServlet.

The response wrapper used by the filter overrides getWriter(), but not
getOutputStream(), because I don't know how to do it.

So the output of S gets collected by WR and is now available to F. And F
calls getWriter() on the original response, and this to my surprise
triggers an IllegalStateException:

  getOutputStream() has already been called for this response

But I haven't called getOutputStream() - I've called getWriter(). The
reason this happens seems to be that the HTML snippet served by the
DefaultServlet gets output using a ServletOutputStream, not a
PrintWriter. I checked the source code, and indeed:

  // Trying to retrieve the servlet output stream
  try { 
      ostream = response.getOutputStream();
  } catch (IllegalStateException e) {
      // If it fails, we try to get a Writer instead if we're
      // trying to serve a text file
      if ( (contentType == null)
              || (contentType.startsWith("text"))
              || (contentType.endsWith("xml")) ) {
          writer = response.getWriter();
      } else {
          throw e;
      }
  }

http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/java/org/apache/catalina/servlets/DefaultServlet.java

So is it the recommended approach in this sort of scenario (or contrived
example) to proceed as observed in the DefaultServlet, by first trying
one of SOS and PW, catch the exception and then try the other one?

Consider how the filter affects the processing here.

When F with WR is used, the call to response.getWriter() in S is
intercepted, the text is written to a buffer, and the DefaultServlet
does the include using an SOS.

When no such filter is used, S calls the real response.getWriter(),
and the include ends up being done using a PW after a caught
IllegalStateException in the DefaultServlet.

The problem arises when a filter is used that passes a response wrapper
containing a buffer on to servlet S. S writes to the buffer, and the
DefaultServlet writes to the real SOS.

So how can I implement getOutputStream() to substitute a buffer? Does
anyone have an implementation?

But even with my Filter overriding both getOutputStream() and
getWriter() and substituting buffers that will be written to, there is a
problem. Servlet S will write to one buffer, and the DefaultServlet will
write to another one. The place of the include set in the source code of
S will be lost. Is this analysis correct?

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Michael Ludwig schrieb am 12.11.2008 um 02:37:29 (+0100):
> But even with my Filter overriding both getOutputStream() and
> getWriter() and substituting buffers that will be written to, there is
> a problem. Servlet S will write to one buffer, and the DefaultServlet
> will write to another one. The place of the include set in the source
> code of S will be lost. Is this analysis correct?

I think the main problem here is starting to collect output in
substituted buffers of response wrappers and not calling the real
getWriter(), or getOutputStream(). Consider this example I've been
learning from:

The Essentials of Filters
http://java.sun.com/products/servlet/Filters.html

public class CharResponseWrapper extends
   HttpServletResponseWrapper {
   private CharArrayWriter output;
   public String toString() {
      return output.toString();
   }
   public CharResponseWrapper(HttpServletResponse response){
      super(response);
      output = new CharArrayWriter();
   }
   public PrintWriter getWriter(){
      return new PrintWriter(output);
   }
}

The problem with this implementation of getWriter() is that it doesn't
call the real implementation in order to force downstream servlets which
may try both of SOS and PW handling the IllegalStateException to do PW
in this case.

So the method should probably be:

   public PrintWriter getWriter() throws IOException {
      PrintWriter orig = super.getWriter();
      return new PrintWriter( output);
   }

That way, the DefaultServlet won't get away with calling
getOutputStream().

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Michael Ludwig schrieb am 20.11.2008 um 01:58:20 (+0100):
> 
> > >    String s = "<!-- Huhu -->" + wrapper.toString();
> > >    ( (HttpServletResponse) res).setHeader(
> > >      "Zeichen", Integer.toString( s.length()));
> > 
> > Note that this may not be correct: other filters could be adding
> > content, and Content-Length is in bytes, not characters. If you are
> > using anything other than ASCII, then this will not be correct.
> 
> Very true.

Or partly true. I wrote "Zeichen" (characters), not "Oktette" (octets,
or bytes), so str.length() is alright.

:-)

Unless I have to deal with Unicode surrogate pairs (which fortunately I
don't). In that exotic case, str.codePointCount() would be required.

John O'Conner's Blog: How long is your String?
http://weblogs.java.net/blog/joconner/archive/2005/08/how_long_is_you.html

In addition to ASCII, the string.length() approach should also work for
8-bit encoding schemes like ISO-8859-1.

SUN simplified like this in their Filters tutorial.

  response.setContentLength(caw.toString().length());

http://java.sun.com/products/servlet/Filters.html

When I have to handle Content-Length myself, for outputting, say,
"Käsekuchen", and I may have chosen UTF-8, looks like I have to use:

  str.getBytes( response.getCharacterEncoding()).length

But as you wrote, unbeknownst to it, my filter may itself be subject to
filtering, so it may not know the definitive answer.

It should probably only set the Content-Length if there is none set yet
and else only apply the delta of what it adds or takes away to the value
already set. Or rather, this should be handled per webapp as suitable,
and there is no general rule.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
I forgot two important lines; explanation in other posts on this thread.

Michael Ludwig schrieb am 26.11.2008 um 02:14:58 (+0100):
>  public ServletOutputStream getOutputStream() throws IOException {
    getResponse().getOutputStream();
>   return stream;
>  }
> 
>  public PrintWriter getWriter() throws IOException {
    getResponse().getWriter();
>   return writer;
>  }

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


RE: [Http]ServletResponseWrapper.getOutputStream()

Posted by "Caldarale, Charles R" <Ch...@unisys.com>.
> From: Michael Ludwig [mailto:milu71@gmx.de]
> Subject: Re: [Http]ServletResponseWrapper.getOutputStream()
>
> Why exactly would the output methods have to be synchronized?

They don't, unless you've got a weird webapp that itself creates multiple threads that generate output.

 - Chuck


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY MATERIAL and is thus for use only by the intended recipient. If you received this in error, please contact the sender and delete the e-mail and its attachments from all computers.

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Martin Gainty schrieb am 22.11.2008 um 12:02:36 (-0500):
> 
> i agree with the unified buffer but i would also make sure all your
> output methods are synchronized

Thanks for your feedback, Martin.

Why exactly would the output methods have to be synchronized? The
request and response objects aren't shared between requests, and a new
instance of a subclass of HttpServletResponseWrapper is created for each
request. And this has no static mutable data. So why synchronized?

> follow the advice on <String/Stream>Writers for incorporating charsets
> and implement encodings
> http://java.sun.com/j2se/1.4.2/docs/api/java/io/OutputStreamWriter.html

It currently looks like this (in case you want to comment):

package milu;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HttpResponseCatcher extends HttpServletResponseWrapper {
 private OutputStream buffer;
 private CapturedServletOutputStream stream;
 private PrintWriter writer;

 // constructor
 public HttpResponseCatcher( HttpServletResponse res) {
  super( res);
  this.buffer = new ByteArrayOutputStream();
  this.stream = new CapturedServletOutputStream( this.buffer);
  this.writer = new PrintWriter( new OutputStreamWriter( this.stream));
 }

 public ServletOutputStream getOutputStream() throws IOException {
  return stream;
 }

 public PrintWriter getWriter() throws IOException {
  return writer;
 }

 public String getCapturedOutput() { return buffer.toString(); }
 public byte[] getByteArray() { return buffer.toString().getBytes(); }
 public char[] getCharArray() { return buffer.toString().toCharArray(); }
}

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


RE: [Http]ServletResponseWrapper.getOutputStream()

Posted by Martin Gainty <mg...@hotmail.com>.
agreed
if you put in log statements which include timings you may see some unplanned interleavings between methods
i agree with the unified buffer but i would also make sure all your output methods are synchronized
follow the advice on <String/Stream>Writers for incorporating charsets and implement encodings
http://java.sun.com/j2se/1.4.2/docs/api/java/io/OutputStreamWriter.html

(pls keep us apprised of output as I would like to know how TC internals are manipulating the response)

Viel Gluck
Martin 
______________________________________________ 
Disclaimer and confidentiality note 
Everything in this e-mail and any attachments relates to the official business of Sender. This transmission is of a confidential nature and Sender does not endorse distribution to any party other than intended recipient. Sender does not necessarily endorse content contained within this transmission. 




> Date: Fri, 21 Nov 2008 22:17:23 +0100
> From: milu71@gmx.de
> To: users@tomcat.apache.org
> Subject: Re: [Http]ServletResponseWrapper.getOutputStream()
> 
> Christopher Schultz schrieb am 20.11.2008 um 10:05:48 (-0500):
> > Micheal,
> > 
> > Your comment about calling getResponse().getOutputStream() and
> > ignoring the result got me to thinking...
> 
> Christopher,
> 
> I may actually have had a point there, although I'm not sure this is the
> case. I just verified that the working HttpResponseCatcher I posted some
> minutes ago stops working correctly when I omit the calls to the
> underlying ServletResponse object. In that case, the output looks like
> this:
> 
> HTTP/1.1 200 OK
> Server: Apache-Coyote/1.1
> Gurke: eingelegt
> GuFilter-Zeichen: 101
> GuFilter-Oktette: 103
> Content-Type: text/html;charset=UTF-8
> Content-Length: 101
> Date: Fri, 21 Nov 2008 20:58:00 GMT
> 
> <!-- Käseküchlein --><p>Included text</p>
> <html><p>Before include</p>
> <p>After include</p></html>
> 
> You can see that the included HTML snippet is in the wrong place. (The
> comment is inserted by the filter on the outgoing response.)
> 
> > You know what? I just realized that I've been leading you down the
> > wrong path: you must manage the buffers separately because of the "you
> > can only call either getOutputStream OR getWriter" rule.
> > 
> > I think you can still make it work with a unified buffer, but you have
> > to be more careful. Let's be less careful and more straightforward.
> 
> Your argument in favour of the unified buffer sounds perfectly logical
> to me. In the end, it's all bytes that get written, regardless of
> whether or not I wrap a PrintWriter around the buffer.
> 
> I have to make sure, of course, to always hand out the same buffer, so
> all output goes there.
> 
> I possibly also have to make sure to play by the same rules as the real
> implementation and not allow both getWriter() and getOutputStream() on
> the same resonse.
> 
> > Basically, you'll have to duplicate your efforts to wrap the Writer
> > the same way you did the OutputStream: create a WriterWrapper and
> > cache that information. I'd recommend using a StringWriter as a
> > backing class, since you don't have to worry too much about the
> > character encoding at that point (because Writers always use
> > characters, not bytes).
> 
> Hmm. Do you still think this is better?
> 
> And what do you mean by "caching" it? Just keeping it in an instance
> variable, instead of erroneously creating a new one for each call of
> getWriter(), as I first did?
> 
> > Anyhow, you definitely /should/ call getResponse().getOutputStream()
> > (or getWriter(), whichever the case may be) when your getOutputStream()
> > method is invoked: you definitely want the state handling to be done by
> > the wrapped request, because you never know what has happened to the
> > request before your filters gets its hands on it.
> 
> I agree: You never know. And who knows what stuff gets set or tweaked in
> Tomcat's internals for each one of getWriter() and getOutputStream(). On
> the other hand, I haven't found any statement to the effect that you
> have to pass through the calls to the underlying response object.
> 
> Michael Ludwig
> 
> ---------------------------------------------------------------------
> To start a new topic, e-mail: users@tomcat.apache.org
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
> 

_________________________________________________________________
Windows Live Hotmail now works up to 70% faster.
http://windowslive.com/Explore/Hotmail?ocid=TXT_TAGLM_WL_hotmail_acq_faster_112008

Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Micheal,

Michael Ludwig wrote:
> Your argument in favour of the unified buffer sounds perfectly logical
> to me. In the end, it's all bytes that get written, regardless of
> whether or not I wrap a PrintWriter around the buffer.

There's always the case that some output has been written before your
filter has been called. In that case, you have to make sure that your
output goes to the correct place. That may mean turning your Writer
contents into an OutputStream, or vice-versa. I think it's simpler to
wrap whichever object the caller is actually requesting.

> I possibly also have to make sure to play by the same rules as the real
> implementation and not allow both getWriter() and getOutputStream() on
> the same resonse.

That's not hard: just make sure you call the wrapped request's getWriter
or getInputStream from your own implementations of the same methods.

> And what do you mean by "caching" it? Just keeping it in an instance
> variable, instead of erroneously creating a new one for each call of
> getWriter(), as I first did?

Yes.

> I agree: You never know. And who knows what stuff gets set or tweaked in
> Tomcat's internals for each one of getWriter() and getOutputStream(). On
> the other hand, I haven't found any statement to the effect that you
> have to pass through the calls to the underlying response object.


No, you don't have to do this, but the benefit of doing it is that you
have the wrapped request enforcing the "call either getWriter or
getInputStream" requirement.

The rule in filters is: only implement what you actually need. Wrap,
baby, wrap. ;)

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkksDDUACgkQ9CaO5/Lv0PDQDwCeL79aNLp4UvJ43UXGU8zHmBqn
HncAn16MaJnKWEDkLQMez+k+44mE8u61
=8el8
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 25.11.2008 um 09:31:17 (-0500):
> Michael Ludwig wrote:
> > Your argument in favour of the unified buffer sounds perfectly
> > logical to me. In the end, it's all bytes that get written,
> > regardless of whether or not I wrap a PrintWriter around the buffer.
> 
> There's always the case that some output has been written before your
> filter has been called. In that case, you have to make sure that your
> output goes to the correct place.

It's not at my discretion to decide where my output should go. Filter or
servlet, I try calling getWriter() or getOutputStream(), and if it that
throws an exception, I call the other. I am unaware of whether what I'm
writing to is the real thing or just a fake an upstream filter has
supplanted. I'm only responsible for things happening downstream. Sort
of like in real life.

Is this thinking flawed?

> That may mean turning your Writer contents into an OutputStream, or
> vice-versa. I think it's simpler to wrap whichever object the caller
> is actually requesting.

But can it be simpler than this:

package milu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HttpResponseCatcher extends HttpServletResponseWrapper {
 private OutputStream buffer;
 private CapturedServletOutputStream stream;
 private PrintWriter writer;

 public HttpResponseCatcher( HttpServletResponse res) {
  super( res);
  this.buffer = new ByteArrayOutputStream();
  this.stream = new CapturedServletOutputStream( this.buffer);
  this.writer = new PrintWriter( new OutputStreamWriter( this.stream));
 }

 public ServletOutputStream getOutputStream() throws IOException {
  getResponse().getOutputStream();
  return stream;
 }

 public PrintWriter getWriter() throws IOException {
  getResponse().getWriter();
  return writer;
 }

 public String getCapturedOutput() { return buffer.toString(); }
 public byte[] getByteArray() { return buffer.toString().getBytes(); }
 public char[] getCharArray() { return buffer.toString().toCharArray(); }
}

> > I agree: You never know. And who knows what stuff gets set or
> > tweaked in Tomcat's internals for each one of getWriter() and
> > getOutputStream(). On the other hand, I haven't found any statement
> > to the effect that you have to pass through the calls to the
> > underlying response object.
> 
> No, you don't have to do this, but the benefit of doing it is that you
> have the wrapped request enforcing the "call either getWriter or
> getInputStream" requirement.

Looks like, yes, I *have* to call getWriter() and getOutputStream() on
the underlying object (and so, ultimately, on the real object, at the
end of the chain), in order to get *correct* behaviour in all scenarios,
including the one discussed here:

* static HTML file served by Tomcat's DefaulServlet
* no flush() prior to include()
* PrintWriter used, not ServletOutputStream

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Micheal,

Michael Ludwig wrote:
> Your argument in favour of the unified buffer sounds perfectly logical
> to me. In the end, it's all bytes that get written, regardless of
> whether or not I wrap a PrintWriter around the buffer.

There's always the case that some output has been written before your
filter has been called. In that case, you have to make sure that your
output goes to the correct place. That may mean turning your Writer
contents into an OutputStream, or vice-versa. I think it's simpler to
wrap whichever object the caller is actually requesting.

> I possibly also have to make sure to play by the same rules as the real
> implementation and not allow both getWriter() and getOutputStream() on
> the same resonse.

That's not hard: just make sure you call the wrapped request's getWriter
or getInputStream from your own implementations of the same methods.

> And what do you mean by "caching" it? Just keeping it in an instance
> variable, instead of erroneously creating a new one for each call of
> getWriter(), as I first did?

Yes.

> I agree: You never know. And who knows what stuff gets set or tweaked in
> Tomcat's internals for each one of getWriter() and getOutputStream(). On
> the other hand, I haven't found any statement to the effect that you
> have to pass through the calls to the underlying response object.


No, you don't have to do this, but the benefit of doing it is that you
have the wrapped request enforcing the "call either getWriter or
getInputStream" requirement.

The rule in filters is: only implement what you actually need. Wrap,
baby, wrap. ;)

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkksDDUACgkQ9CaO5/Lv0PDQDwCeL79aNLp4UvJ43UXGU8zHmBqn
HncAn16MaJnKWEDkLQMez+k+44mE8u61
=8el8
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 20.11.2008 um 10:05:48 (-0500):
> Micheal,
> 
> Your comment about calling getResponse().getOutputStream() and
> ignoring the result got me to thinking...

Christopher,

I may actually have had a point there, although I'm not sure this is the
case. I just verified that the working HttpResponseCatcher I posted some
minutes ago stops working correctly when I omit the calls to the
underlying ServletResponse object. In that case, the output looks like
this:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Gurke: eingelegt
GuFilter-Zeichen: 101
GuFilter-Oktette: 103
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Fri, 21 Nov 2008 20:58:00 GMT

<!-- Käseküchlein --><p>Included text</p>
<html><p>Before include</p>
<p>After include</p></html>

You can see that the included HTML snippet is in the wrong place. (The
comment is inserted by the filter on the outgoing response.)

> You know what? I just realized that I've been leading you down the
> wrong path: you must manage the buffers separately because of the "you
> can only call either getOutputStream OR getWriter" rule.
> 
> I think you can still make it work with a unified buffer, but you have
> to be more careful. Let's be less careful and more straightforward.

Your argument in favour of the unified buffer sounds perfectly logical
to me. In the end, it's all bytes that get written, regardless of
whether or not I wrap a PrintWriter around the buffer.

I have to make sure, of course, to always hand out the same buffer, so
all output goes there.

I possibly also have to make sure to play by the same rules as the real
implementation and not allow both getWriter() and getOutputStream() on
the same resonse.

> Basically, you'll have to duplicate your efforts to wrap the Writer
> the same way you did the OutputStream: create a WriterWrapper and
> cache that information. I'd recommend using a StringWriter as a
> backing class, since you don't have to worry too much about the
> character encoding at that point (because Writers always use
> characters, not bytes).

Hmm. Do you still think this is better?

And what do you mean by "caching" it? Just keeping it in an instance
variable, instead of erroneously creating a new one for each call of
getWriter(), as I first did?

> Anyhow, you definitely /should/ call getResponse().getOutputStream()
> (or getWriter(), whichever the case may be) when your getOutputStream()
> method is invoked: you definitely want the state handling to be done by
> the wrapped request, because you never know what has happened to the
> request before your filters gets its hands on it.

I agree: You never know. And who knows what stuff gets set or tweaked in
Tomcat's internals for each one of getWriter() and getOutputStream(). On
the other hand, I haven't found any statement to the effect that you
have to pass through the calls to the underlying response object.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by André Warnier <aw...@ice-sa.com>.
> 
>> You should post your entire filter next time, if only so André can see
>> it ;)
> 
> Yes, I'll resubmit it to your scrutiny when it's ready ...
> 
I already saw it, copied it, saved it, studied it and sold it to a customer.
Now you guys are telling me it's not working ?

:-)

I did put Michael's name in it though.
// Captured on Tomcat users list
// Author : Michael Ludwig - 2008/11/21
import java.io.*;
...

I must admit that Michael's code and your discussions here (way over my 
head) have definitely convinced me that I need a couple of years more of 
Java studies before I attempt the agnostic POST filter.



---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 20.11.2008 um 10:05:48 (-0500):

> Keep reading for more.
> 
> Michael Ludwig wrote:
> >> So, when this code is called from an "include" call to the request
> >> dispatcher, it doesn't appear in your filter's captured output? Or,
> >> it doesn't appear in the final response sent to the browser (or
> >> both)?
> > 
> > Both.
> 
> Okay, but only when using Writer, right? OutputStream works properly?

Yes, OutputStream worked properly.

> > Closing the stream would flush it, wouldn't it?
> 
> It should, but it doesn't hurt to be tidy. I seem to remember C
> library routines that would truncate output if you didn't explicitly
> flush the buffers.

Okay. True, it doesn't hurt to be tidy.

> >> You could even add a method to your wrapper that will tell you
> >> which style of output is being used on the response: output stream
> >> versus writer. Then, you could avoid the try/catch which will make
> >> your code run a bit faster. Faster filters are always better than
> >> slow ones ;)

> See Chuck's response for performance considerations. IMO, since you
> have the opportunity to avoid the exception, you may as well take it.

I may add it. But DefaultServlet will never know about it. It will
continue to first call getOutputStream() and handle the exception by
calling getWriter().

> >>>  public void flushBuffer() throws IOException {
> >>>   this.buffer.flush();
> >>>  }
> >> Flushing a ByteArrayOutputStream doesn't do anything. What you
> >> really want to do is flush all the OutputStream and Writer objects
> >> you've created when calls to getOutputStream and getWriter come in.
> > 
> > Okay.
> 
> Oh, and then call getResponse().flushBuffer(), too. The caller expects
> that the flush goes all the way back to the real client response.

I don't understand where I should call getResponse().flushBuffer().
In HttpResponseCatcher.flushBuffer(), as above?

> >>>  public void write( byte[] b) throws IOException {

> >> Do you get stack traces printing from this method?
> > 
> > No.
> > 
> >>>  public void write( byte[] b, int off, int len) throws IOException {

> >> How about this one?
> > 
> > Yes.
> 
> That's good. I guess the caller is preferring to use sections of a
> byte buffer instead of just a bare one. That's not surprising, and
> shouldn't itself be an indication of a problem. If you look at the
> code for DefaultServlet, you probably will see calls to write(byte[],
> int, int) and none to write(byte[]).

Yes, that's true. The latter might be implemented in terms of the
former. As the length is already known when calling, the call can also
be made to the real method straight away.

> > Thanks for this suggestion [of using TeeOutputStream]. My intent is
> > simply to understand servlets. On hitting this include oddity, I
> > just decided to track it down, thinking I would learn from it.
> > (Which I'm doing thanks to your help.)
> 
> Learning is always good. Hopefully you'll lurk on the list from here
> on out. :)

Sure, time permitting. :-)

> You should post your entire filter next time, if only so André can see
> it ;)

Yes, I'll resubmit it to your scrutiny when it's ready ...

Thanks for all the help.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Micheal,

Your comment about calling getResponse().getOutputStream() and ignoring
the result got me to thinking...

You know what? I just realized that I've been leading you down the wrong
path: you must manage the buffers separately because of the "you can
only call either getOutputStream OR getWriter" rule.

I think you can still make it work with a unified buffer, but you have
to be more careful. Let's be less careful and more straightforward.

Basically, you'll have to duplicate your efforts to wrap the Writer the
same way you did the OutputStream: create a WriterWrapper and cache that
information. I'd recommend using a StringWriter as a backing class,
since you don't have to worry too much about the character encoding at
that point (because Writers always use characters, not bytes).

Anyhow, you definitely /should/ call getResponse().getOutputStream() (or
getWriter(), whichever the case may be) when your getOutputStream()
method is invoked: you definitely want the state handling to be done by
the wrapped request, because you never know what has happened to the
request before your filters gets its hands on it.

Keep reading for more.

Michael Ludwig wrote:
>> So, when this code is called from an "include" call to the request
>> dispatcher, it doesn't appear in your filter's captured output? Or, it
>> doesn't appear in the final response sent to the browser (or both)?
> 
> Both.

Okay, but only when using Writer, right? OutputStream works properly?

>> You might want to flush() before close() but that shouldn't matter too
>> much.
> 
> Closing the stream would flush it, wouldn't it?

It should, but it doesn't hurt to be tidy. I seem to remember C library
routines that would truncate output if you didn't explicitly flush the
buffers.

>> You could even add a method to your wrapper that will tell you which
>> style of output is being used on the response: output stream versus
>> writer. Then, you could avoid the try/catch which will make your code
>> run a bit faster. Faster filters are always better than slow ones ;)
> 
> Try/catch can't be that much of a problem, can it? Isn't it just a fancy
> way of conditional branching with information attached? As a language
> feature, I assume it doesn't entail a performance hit?

See Chuck's response for performance considerations. IMO, since you have
the opportunity to avoid the exception, you may as well take it.

>>>  // http://marc.info/?l=tomcat-user&m=109913615025298
>>>  public void flushBuffer() throws IOException {
>>>   this.buffer.flush();
>>>  }
>> Flushing a ByteArrayOutputStream doesn't do anything. What you really
>> want to do is flush all the OutputStream and Writer objects you've
>> created when calls to getOutputStream and getWriter come in.
> 
> Okay.

Oh, and then call getResponse().flushBuffer(), too. The caller expects
that the flush goes all the way back to the real client response.

>>>  public void write( byte[] b) throws IOException {
>>>   new Throwable().printStackTrace();
>>>   this.buffer.write( b);
>>>  }
>> Do you get stack traces printing from this method?
> 
> No.
> 
>>>  public void write( byte[] b, int off, int len) throws IOException {
>>>   new Throwable().printStackTrace();
>>>   this.buffer.write( b, off, len);
>>>  }
>> How about this one?
> 
> Yes.

That's good. I guess the caller is preferring to use sections of a byte
buffer instead of just a bare one. That's not surprising, and shouldn't
itself be an indication of a problem. If you look at the code for
DefaultServlet, you probably will see calls to write(byte[], int, int)
and none to write(byte[]).

> Thanks for this suggestion [of using TeeOutputStream]. My intent is simply to understand servlets.
> On hitting this include oddity, I just decided to track it down,
> thinking I would learn from it. (Which I'm doing thanks to your help.)

Learning is always good. Hopefully you'll lurk on the list from here on
out. :)

>> So, can you walk me through what actually happens when you use this,
>> again? I think we've become lost in the details.
> 
> Yes, a lot of details. I'm going to report back with a revised version
> based on your suggestions, and probably further investigation.
> 
> The problem is manifest in that the file included via
> RequestDispatcher.include() and then processed by Tomcat's
> DefaultServlet does not appear in either the buffer substituted
> in my response wrapper nor the output when the output method
> chosen is PrintWriter rather than ServletOutputStream. With SOS,
> everything seems to work fine.

Okay, good. I think my first suggestion (stop using a unified buffer) is
the right way to do things: you'll handle each type of output strategy
separately and I believe you'll have better results. You should post
your entire filter next time, if only so André can see it ;)

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkklfMwACgkQ9CaO5/Lv0PC9jACgrgIZad3hhYirKE2UsSQOmU09
U9MAnjpquAJb5pXh3ehBvVBtMOgYGlqC
=840h
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Caldarale, Charles R schrieb am 19.11.2008 um 19:45:37 (-0600):
> 
> > Try/catch can't be that much of a problem, can it? Isn't it
> > just a fancy way of conditional branching with information
> > attached?
> 
> Sorry, but no.  The throwing of an exception causes the current block
> to terminate abruptly and transfer control to JVM-generated code that
> searches a catch block table for the address of the failure, and then
> does further searches looking for a matching exception type.  If no
> match is found, the current stack frame is popped off, and the process
> repeats with the calling method's catch block table.  Although modern
> JITs are better at doing this than pre-HotSpot ones, it's still a lot
> more expensive than a simple test.

Thanks a lot for this clarification. I was unaware of this. It seems to
have been true for more than eight years ... Well, probably since Java's
inception.

  Exceptions in Java: Nothing exceptional about them - JavaWorld
  http://www.javaworld.com/javaworld/jw-08-2000/jw-0818-exceptions.html
  (pages 4 and 5 on the performance aspect)

  In short, expending the Java VM to handle a thrown exception requires
  more effort, i.e., abrupt method completion is significantly more
  expensive (performance-wise) than a normal method completion.

So Christopher's suggestion to add method and property to detect which
one of getWriter() and getOutputStream() was called makes sense for
situations where I have many nested filters and have them all detect
the PW/SOS issue by calling my method in order to avoid risking an
exception.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


RE: [Http]ServletResponseWrapper.getOutputStream()

Posted by "Caldarale, Charles R" <Ch...@unisys.com>.
> From: Michael Ludwig [mailto:milu71@gmx.de]
> Subject: Re: [Http]ServletResponseWrapper.getOutputStream()

> Try/catch can't be that much of a problem, can it? Isn't it
> just a fancy way of conditional branching with information
> attached?

Sorry, but no.  The throwing of an exception causes the current block to terminate abruptly and transfer control to JVM-generated code that searches a catch block table for the address of the failure, and then does further searches looking for a matching exception type.  If no match is found, the current stack frame is popped off, and the process repeats with the calling method's catch block table.  Although modern JITs are better at doing this than pre-HotSpot ones, it's still a lot more expensive than a simple test.

 - Chuck


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY MATERIAL and is thus for use only by the intended recipient. If you received this in error, please contact the sender and delete the e-mail and its attachments from all computers.

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Hi Christopher,

thanks for a long and detailed response.

Christopher Schultz schrieb am 18.11.2008 um 16:26:23 (-0500):
> Michael Ludwig wrote:

> > I thought it helpful to have a trace of the methods getting called

> One way to do it is to use a "proxy object". If you haven't used proxies
> before, basically Java allows you to create a magic object at runtime

That's an interesting technique. I've written a small standalone test
program to understand it, and it works nicely. I'm going to give this a
try in the Tomcat context and report back.

> So, when this code is called from an "include" call to the request
> dispatcher, it doesn't appear in your filter's captured output? Or, it
> doesn't appear in the final response sent to the browser (or both)?

Both.

> > PrintWriter out = res.getWriter(); // include doesn't appear in output
> > // ServletOutputStream out = res.getOutputStream(); // works
> > [...]
> > out.close();
> 
> You might want to flush() before close() but that shouldn't matter too
> much.

Closing the stream would flush it, wouldn't it?

> >    String s = "<!-- Huhu -->" + wrapper.toString();
> >    ( (HttpServletResponse) res).setHeader(
> >      "Zeichen", Integer.toString( s.length()));
> 
> Note that this may not be correct: other filters could be adding
> content, and Content-Length is in bytes, not characters. If you are
> using anything other than ASCII, then this will not be correct.

Very true.

> > [filter code]

> Note that there's nothing specific to HTTP at this point (except for
> the headers), so you might want to consider making this a bit more
> generic -- so you can capture the output of /any/ response, not just
> HTTP responses.

I'll try and take this into consideration.

> You could even add a method to your wrapper that will tell you which
> style of output is being used on the response: output stream versus
> writer. Then, you could avoid the try/catch which will make your code
> run a bit faster. Faster filters are always better than slow ones ;)

Try/catch can't be that much of a problem, can it? Isn't it just a fancy
way of conditional branching with information attached? As a language
feature, I assume it doesn't entail a performance hit?

> > public class HttpResponseCatcher extends HttpServletResponseWrapper {
> > 
> >  [...]
> > 
> >  public ServletOutputStream getOutputStream() throws IOException {
> >   [...]
> >   this.getResponse().getOutputStream();
> >   return new CapturedServletOutputStream( this.buffer);
> >  }
> 
> You might not want to call this.getResponse().getOutputStream(). There's
> no need to do it, actually.

If everything worked, all output would be written to my unified buffer.
But it doesn't. I'm calling this method so the real object registers the
call and on subsequent calls to getWriter() an exception is triggered,
which is then caught to invoke getOutputStream(). Or vice versa.

So this is basically for debugging purposes.

> >  public PrintWriter getWriter() throws IOException {
> >   [...]
> >   return new PrintWriter(
> >     new OutputStreamWriter(
> >      new CapturedServletOutputStream( this.buffer)));
> >  }
> 
> You might actually want to use a single CapturedServletOutputStream or
> OutputStreamWriter for all calls, rather than constructing a new one
> each time. Otherwise, you might get strange behavior and weird output
> ordering.

Sounds reasonable.

> >  // http://marc.info/?l=tomcat-user&m=109913615025298
> >  public void flushBuffer() throws IOException {
> >   this.buffer.flush();
> >  }
> 
> Flushing a ByteArrayOutputStream doesn't do anything. What you really
> want to do is flush all the OutputStream and Writer objects you've
> created when calls to getOutputStream and getWriter come in.

Okay.

> >  public String toString()     { return this.buffer.toString(); }
> 
> Although this will work, it might be surprising to users of your class.
> I would like a method such as "getCapturedOutput" instead.

True, that's much better.

> > public class CapturedServletOutputStream extends ServletOutputStream {
> > 
> >  ByteArrayOutputStream buffer; // Puffern, was geschrieben wird.
> 
> I would base this on an OutputStream, not a ByteArrayOutputStream
> specifically. This will make your class more generic and more useful.

Better style, at least.

> >  public void write( byte[] b) throws IOException {
> >   new Throwable().printStackTrace();
> >   this.buffer.write( b);
> >  }
> 
> Do you get stack traces printing from this method?

No.

> >  public void write( byte[] b, int off, int len) throws IOException {
> >   new Throwable().printStackTrace();
> >   this.buffer.write( b, off, len);
> >  }
> 
> How about this one?

Yes.

> If your intent is to capture a copy of the outgoing response, perhaps
> some of this work has already been done for you. Check out Jakarta
> commons-io's TeeOutputStream, which allows you to split an OutputStream
> into two OutputStreams. I think this would save you some code writing
> for yourself. Then again, having to subclass ServletOutputStream would
> basically mean duplicating TeeOutputStream in the first place. Hmm. Just
> a thought.

Thanks for this suggestion. My intent is simply to understand servlets.
On hitting this include oddity, I just decided to track it down,
thinking I would learn from it. (Which I'm doing thanks to your help.)

> So, can you walk me through what actually happens when you use this,
> again? I think we've become lost in the details.

Yes, a lot of details. I'm going to report back with a revised version
based on your suggestions, and probably further investigation.

The problem is manifest in that the file included via
RequestDispatcher.include() and then processed by Tomcat's
DefaultServlet does not appear in either the buffer substituted
in my response wrapper nor the output when the output method
chosen is PrintWriter rather than ServletOutputStream. With SOS,
everything seems to work fine.

Thanks a lot, once more.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 01.12.2008 um 13:15:24 (-0500):
> Michael Ludwig wrote:

[Better late than never.]

> >  public HttpResponseCatcher( HttpServletResponse res) {
> >   super( res);
> >   this.buffer = new ByteArrayOutputStream();
> >   this.stream = new CapturedServletOutputStream( this.buffer);
> >   this.writer = new PrintWriter( new OutputStreamWriter( this.stream));
> 
> ! No character encoding!

Yes, the ctor OutputStreamWriter(OutputStream out, String charsetName)
should be used, so:

  new OutputStreamWriter( this.stream, this.getCharacterEncoding())

> I would lazily instantiate these objects -- at least the stream and
> writer. There's no need to create a writer if the caller doesn't ask
> for one.

Okay, probably like this:

  private ByteArrayOutputStream buffer;
  private CapturedServletOutputStream stream;

  private StringWriter stringWriter;
  private PrintWriter writer;

  public ServletOutputStream getOutputStream() throws IOException {
    getResponse().getOutputStream();
    if ( buffer == null) {
      buffer = new ByteArrayOutputStream();
      stream = new CapturedServletOutputStream( buffer);
    }
    return stream;
  }

  public PrintWriter getWriter() throws IOException {
    getResponse().getWriter();
    if ( stringWriter == null ) {
      stringWriter = new StringWriter();
      writer = new PrintWriter( stringWriter);
    }
    return writer;
  }

I'm using a StringWriter as a backing class here, following a
recommendation you made in a previous post (20.11.08).

> Also, the caller might want to set the character encoding on the
> response, and you have to allow that to occur before you create your
> writer.

Now has to be handled by the underlying response object.

> >  public byte[] getByteArray() { return buffer.toString().getBytes(); }
> 
> Yikes! You aren't checking the character encoding of the response! You
> definitely need to do this in order to properly implement this method.

Yes, this should be:

    return buffer.toString( this.getCharacterEncoding()).getBytes()

And "buffer" in this case has to be of type ByteArrayOutputStream, not
just OutputStream.

Thanks for all your help,

Michael Ludwig

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael,

Michael Ludwig wrote:
> [Handing out multiple objects] seems to have caused the effect observed, which was the absence of
> the HTML file included via RequestDispatcher.include() in both the
> buffer used to capture the response and the output itself.
> 
> Might this have to do with the fact that the PrintWriter is buffered?

Yes. But you want all client code to be working on the same wrapper
object as well as underlying object. There's no compelling reason to
create additional objects.

>>>  public void flushBuffer() throws IOException {
>>>   this.buffer.flush();
>>>  }
>> Flushing a ByteArrayOutputStream doesn't do anything. What you really
>> want to do is flush all the OutputStream and Writer objects you've
>> created when calls to getOutputStream and getWriter come in.
> 
> I dropped this method.

I would put it back. You want the Response.flushBuffer method to work
and actually flush the buffer being used. You should add this method back.

>  public HttpResponseCatcher( HttpServletResponse res) {
>   super( res);
>   this.buffer = new ByteArrayOutputStream();
>   this.stream = new CapturedServletOutputStream( this.buffer);
>   this.writer = new PrintWriter( new OutputStreamWriter( this.stream));

! No character encoding!

>  }

I would lazily instantiate these objects -- at least the stream and
writer. There's no need to create a writer if the caller doesn't ask for
one. Also, the caller might want to set the character encoding on the
response, and you have to allow that to occur before you create your writer.

>  public ServletOutputStream getOutputStream() throws IOException {
>   getResponse().getOutputStream();
>   return stream;
>  }

Here's where you'd create your stream (or writer).

 public ServletOutputStream getOutputStream() throws IOException {
  getResponse().getOutputStream();

  if(null == stream)
     stream = new CapturedServletOutputStream(this.buffer);

  return stream;
 }

I would do the same with the PrintWriter. I still think that using a
unified buffer is non-ideal. But hey... if it works, it works.

>  public String getCapturedOutput() { return buffer.toString(); }
>  public byte[] getByteArray() { return buffer.toString().getBytes(); }

Yikes! You aren't checking the character encoding of the response! You
definitely need to do this in order to properly implement this method.

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkk0KbwACgkQ9CaO5/Lv0PCeKACgnbfYEZveOuhu8C2THl8oaUg+
uOcAnjcYQImpXYmQoY5BAdKsyUf7Bgwm
=f1dV
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 18.11.2008 um 16:26:23 (-0500):

> > public class HttpResponseCatcher extends HttpServletResponseWrapper {
> > 
> >  private ByteArrayOutputStream buffer;
> > 
> >  public HttpResponseCatcher( HttpServletResponse res) {
> >   super( res);
> >   this.buffer = new ByteArrayOutputStream();
> >  }
> > 
> >  public ServletOutputStream getOutputStream() throws IOException {
> >   this.getResponse().getOutputStream();
> >   return new CapturedServletOutputStream( this.buffer);
> >  }

> >  public PrintWriter getWriter() throws IOException {
> >   this.getResponse().getWriter();
> >   return new PrintWriter(
> >     new OutputStreamWriter(
> >      new CapturedServletOutputStream( this.buffer)));
> >  }
> 
> You might actually want to use a single CapturedServletOutputStream or
> OutputStreamWriter for all calls, rather than constructing a new one
> each time. Otherwise, you might get strange behavior and weird output
> ordering.

This seems to have caused the effect observed, which was the absence of
the HTML file included via RequestDispatcher.include() in both the
buffer used to capture the response and the output itself.

Might this have to do with the fact that the PrintWriter is buffered?

I'm attaching the new version, which seems to work.

> >  public void flushBuffer() throws IOException {
> >   this.buffer.flush();
> >  }
> 
> Flushing a ByteArrayOutputStream doesn't do anything. What you really
> want to do is flush all the OutputStream and Writer objects you've
> created when calls to getOutputStream and getWriter come in.

I dropped this method.

> >  public String toString()     { return this.buffer.toString(); }
> 
> Although this will work, it might be surprising to users of your class.
> I would like a method such as "getCapturedOutput" instead.

I renamed this one.

Thanks for your help, Christopher and Chuck!

Michael Ludwig

package milu;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HttpResponseCatcher extends HttpServletResponseWrapper {

 private OutputStream buffer;
 private CapturedServletOutputStream stream;
 private PrintWriter writer;

 public HttpResponseCatcher( HttpServletResponse res) {
  super( res);
  this.buffer = new ByteArrayOutputStream();
  this.stream = new CapturedServletOutputStream( this.buffer);
  this.writer = new PrintWriter( new OutputStreamWriter( this.stream));
 }

 public ServletOutputStream getOutputStream() throws IOException {
  getResponse().getOutputStream();
  return stream;
 }

 public PrintWriter getWriter() throws IOException {
  getResponse().getWriter();
  /* Nicht jedesmal einen neuen PrintWriter erzeugen, sonst k÷nnen
   * Daten verloren gehen. */
  // return new PrintWriter( new OutputStreamWriter( stream));
  // return new PrintWriter( new OutputStreamWriter( stream), true);
  // The above two lines may lead to incomplete output as explained.
  return writer; // This seems to be correct.
 }

 public String getCapturedOutput() { return buffer.toString(); }
 public byte[] getByteArray() { return buffer.toString().getBytes(); }
 public char[] getCharArray() { return buffer.toString().toCharArray(); }
}

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael,

Michael Ludwig wrote:
> It's not clear to me which methods I have to implement in order to see
> the included static resource processed by Tomcat's DefaultServlet appear
> in the output regardless of the output method (PW or SOS) being chosen.

If you properly implement getWriter and getOutputStream in your wrapped
request, things should work properly. My guess is that you are
swallowing some output somewhere.

> I can see that in the case of the PrintWriter, the output lacks the
> included static resource. I suspect the bug somewhere between my filter
> and my servlet. Now in order to determine where exactly it is, I thought
> it helpful to have a trace of the methods getting called, particularly
> for the processing of the included resource by the DefaultServlet. It
> may be that I'm not implementing a method I need to implement, and in
> that case writing my own logging statements wouldn't tell the whole
> story.

One way to do it is to use a "proxy object". If you haven't used proxies
before, basically Java allows you to create a magic object at runtime
that implements an interface (actually, any number of interfaces) and
dispatches all calls to a single "doit" type method. You can do anything
you want after that.

Try something like this in your filter:

public void doFilter(...)
{
   final HttpServletResponse myResponse = (HttpServletResponse)response;

   InvocationHandler handler = new InvocationHandler()
       {
          public Object invoke(Object proxy, Method method, Object[] a)
          {
             System.err.println("Calling " + method);

             method.invoke(myResponse, a);
          }
       };

    HttpServletResponse wrapper =
(HttpServletResponse)Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[] { HttpServletResponse.class }, handler);

   chain.doFilter(req, wrapper);
}

Of course, there are all kinds of imports to declare and exceptions to
handle. Look in the java.lang.reflect package for most of the things you
need to import.

So, when this code is called from an "include" call to the request
dispatcher, it doesn't appear in your filter's captured output? Or, it
doesn't appear in the final response sent to the browser (or both)?

>   PrintWriter out = res.getWriter(); // include doesn't appear in output
>   // ServletOutputStream out = res.getOutputStream(); // works
>   out.println( "<html><p>Before include</p>");
>   RequestDispatcher rd =
>    this.getServletContext().getRequestDispatcher( "/include.html");
>   rd.include( req, res);
>   out.println( "<p>After include</p></html>");
>   out.close();

You might want to flush() before close() but that shouldn't matter too much.

>  public void doFilter(
>    ServletRequest req, ServletResponse res, FilterChain chain)
>   throws IOException, ServletException
>  {
>   if ( res instanceof HttpServletResponse ) {
>    ( (HttpServletResponse) res).setHeader( "Gurke", "eingelegt");
> 
>    HttpResponseCatcher wrapper =
>     new HttpResponseCatcher( (HttpServletResponse) res);
>    chain.doFilter( req, wrapper);

Looks good so far.

>    String s = "<!-- Huhu -->" + wrapper.toString();
>    ( (HttpServletResponse) res).setHeader(
>      "Zeichen", Integer.toString( s.length()));

Note that this may not be correct: other filters could be adding
content, and Content-Length is in bytes, not characters. If you are
using anything other than ASCII, then this will not be correct.

>    try {
>     ServletOutputStream out = res.getOutputStream();
>     out.print( s);
>     out.close();
>    }
>    catch ( IllegalStateException ise ) {
>     PrintWriter out = res.getWriter();
>     out.write( s);
>     out.close();
>    }

This looks like it ought to work, but I haven't seen
HttpResponseCatcher, yet. Note that there's nothing specific to HTTP at
this point (except for the headers), so you might want to consider
making this a bit more generic -- so you can capture the output of /any/
response, not just HTTP responses.

You could even add a method to your wrapper that will tell you which
style of output is being used on the response: output stream versus
writer. Then, you could avoid the try/catch which will make your code
run a bit faster. Faster filters are always better than slow ones ;)

> public class HttpResponseCatcher extends HttpServletResponseWrapper {
> 
>  private ByteArrayOutputStream buffer;
> 
>  public HttpResponseCatcher( HttpServletResponse res) {
>   super( res);
>   this.buffer = new ByteArrayOutputStream();
>  }
> 
>  public ServletOutputStream getOutputStream() throws IOException {
>   new Throwable("######## STROM").printStackTrace();
>   // siehe DefaultServlet.serveResource(), dort Aufruf von getOutputStream()
>   this.getResponse().getOutputStream();
>   return new CapturedServletOutputStream( this.buffer);
>  }

You might not want to call this.getResponse().getOutputStream(). There's
no need to do it, actually.

>  public PrintWriter getWriter() throws IOException {
>   new Throwable("######## SCHREIBER").printStackTrace();
>   this.getResponse().getWriter();
>   return new PrintWriter(
>     new OutputStreamWriter(
>      new CapturedServletOutputStream( this.buffer)));
>  }

You might actually want to use a single CapturedServletOutputStream or
OutputStreamWriter for all calls, rather than constructing a new one
each time. Otherwise, you might get strange behavior and weird output
ordering.

>  // http://marc.info/?l=tomcat-user&m=109913615025298
>  public void flushBuffer() throws IOException {
>   this.buffer.flush();
>  }

Flushing a ByteArrayOutputStream doesn't do anything. What you really
want to do is flush all the OutputStream and Writer objects you've
created when calls to getOutputStream and getWriter come in.

>  public String toString()     { return this.buffer.toString(); }

Although this will work, it might be surprising to users of your class.
I would like a method such as "getCapturedOutput" instead.

> public class CapturedServletOutputStream extends ServletOutputStream {
> 
>  ByteArrayOutputStream buffer; // Puffern, was geschrieben wird.

I would base this on an OutputStream, not a ByteArrayOutputStream
specifically. This will make your class more generic and more useful.

>  CapturedServletOutputStream( ByteArrayOutputStream buf) {
>   this.buffer = buf;
>  }
> 
>  public void write( int b) throws IOException {
>   this.buffer.write( b);
>  }
>  public void write( byte[] b) throws IOException {
>   new Throwable().printStackTrace();
>   this.buffer.write( b);
>  }

Do you get stack traces printing from this method?

>  public void write( byte[] b, int off, int len) throws IOException {
>   new Throwable().printStackTrace();
>   this.buffer.write( b, off, len);
>  }

How about this one?

If your intent is to capture a copy of the outgoing response, perhaps
some of this work has already been done for you. Check out Jakarta
commons-io's TeeOutputStream, which allows you to split an OutputStream
into two OutputStreams. I think this would save you some code writing
for yourself. Then again, having to subclass ServletOutputStream would
basically mean duplicating TeeOutputStream in the first place. Hmm. Just
a thought.

So, can you walk me through what actually happens when you use this,
again? I think we've become lost in the details.

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkjMv8ACgkQ9CaO5/Lv0PDu6QCghdp/aZt1nlFxJ43MZF9zGuel
UFIAn1CimanNyvVXxKbO0tPvBAqGZnGQ
=IjrZ
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Michael Ludwig schrieb am 17.11.2008 um 22:34:16 (+0100):
> * ../include.html
> * web.xml
> * WEB-INF/src/milu/IncludeServlet.java
> * WEB-INF/src/milu/GuFilter.java
> * WEB-INF/src/milu/HttpResponseCatcher.java
> * WEB-INF/src/milu/CapturedServletOutputStream.java

Correction: I'm in WEB-INF here, so the leading WEB-INF path component
on the paths to the Java files is actually misleading and shouldn't be
there.

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 16.11.2008 um 19:43:00 (-0500):
> Michael Ludwig wrote:
> > Christopher Schultz schrieb am 12.11.2008 um 14:46:08 (-0500):
> >> What you need to do is provide a unified buffer that /both/ calls
> >> can write to. If you use a ByteArrayOutputStream at the lowest
> >> level and then wrap that in an OutputStreamWriter for calls to
> >> getWriter(), you should be able to handle arbitrary use of your
> >> wrapper.
> > 
> > This sounds very reasonable. I followed this road. Unfortunately,
> > the included resource still is written elsewhere. Or maybe not
> > flushed?
> 
> What do you mean, it's still written elsewhere?

Christopher,

the included HTML snippet handled by the DefaultServlet does not appear
in the output when done via the PrintWriter. I don't know where it ends
up being written in that case. I assume it is being written elsewhere,
rather than not being written at all.

> If you can wrap the object that's being called (like the request, or
> the output stream/writer) then you can write your own logging
> statements at appropriate times.

It's not clear to me which methods I have to implement in order to see
the included static resource processed by Tomcat's DefaultServlet appear
in the output regardless of the output method (PW or SOS) being chosen.

I can see that in the case of the PrintWriter, the output lacks the
included static resource. I suspect the bug somewhere between my filter
and my servlet. Now in order to determine where exactly it is, I thought
it helpful to have a trace of the methods getting called, particularly
for the processing of the included resource by the DefaultServlet. It
may be that I'm not implementing a method I need to implement, and in
that case writing my own logging statements wouldn't tell the whole
story.

So if this is possible, I'd like to know how to enable this or, of
course, be told in what ways my thinking is mistaken, or simply besides
the point.

To give you more food for the latter option in case you're curious
enough, here's the code, six files altogether. At any rate, thanks
a lot for your continued interest!

Michael Ludwig

* ../include.html
* web.xml
* WEB-INF/src/milu/IncludeServlet.java
* WEB-INF/src/milu/GuFilter.java
* WEB-INF/src/milu/HttpResponseCatcher.java
* WEB-INF/src/milu/CapturedServletOutputStream.java

:: more /t1 ..\include.html
<p>Included text</p>

:: more /t1 web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 version="2.5">

 <servlet>
  <servlet-name>include</servlet-name>
  <servlet-class>milu.IncludeServlet</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>include</servlet-name>
  <url-pattern>/include</url-pattern>
 </servlet-mapping>

 <servlet>
  <servlet-name>include2</servlet-name>
  <servlet-class>milu.IncludeServlet</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>include2</servlet-name>
  <url-pattern>/include2</url-pattern>
 </servlet-mapping>

 <filter>
  <filter-name>Gu</filter-name>
  <filter-class>milu.GuFilter</filter-class>
 </filter>

 <filter-mapping>
  <filter-name>Gu</filter-name>
  <servlet-name>include</servlet-name>
 </filter-mapping>

</web-app>

:: more /t1 src\milu\IncludeServlet.java
package milu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class IncludeServlet extends HttpServlet {
 public void doGet (HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
 {
  res.setContentType("text/html; charset=UTF-8");
  PrintWriter out = res.getWriter(); // include doesn't appear in output
  // ServletOutputStream out = res.getOutputStream(); // works
  out.println( "<html><p>Before include</p>");
  RequestDispatcher rd =
   this.getServletContext().getRequestDispatcher( "/include.html");
  rd.include( req, res);
  out.println( "<p>After include</p></html>");
  out.close();
 }
}

:: more /t1 src\milu\GuFilter.java
package milu;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public final class GuFilter implements Filter {

 private FilterConfig config;
 public void init( FilterConfig config) { this.config = config; }
 public void destroy()                  { this.config = null;   }

 public void doFilter(
   ServletRequest req, ServletResponse res, FilterChain chain)
  throws IOException, ServletException
 {
  if ( res instanceof HttpServletResponse ) {
   ( (HttpServletResponse) res).setHeader( "Gurke", "eingelegt");

   HttpResponseCatcher wrapper =
    new HttpResponseCatcher( (HttpServletResponse) res);
   chain.doFilter( req, wrapper);

   String s = "<!-- Huhu -->" + wrapper.toString();
   ( (HttpServletResponse) res).setHeader(
     "Zeichen", Integer.toString( s.length()));

   try {
    ServletOutputStream out = res.getOutputStream();
    out.print( s);
    out.close();
   }
   catch ( IllegalStateException ise ) {
    PrintWriter out = res.getWriter();
    out.write( s);
    out.close();
   }
  }
  else {
   chain.doFilter( req, res);
  }
 }
}

:: more /t1 src\milu\HttpResponseCatcher.java
package milu;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HttpResponseCatcher extends HttpServletResponseWrapper {

 private ByteArrayOutputStream buffer;

 public HttpResponseCatcher( HttpServletResponse res) {
  super( res);
  this.buffer = new ByteArrayOutputStream();
 }

 public ServletOutputStream getOutputStream() throws IOException {
  new Throwable("######## STROM").printStackTrace();
  // siehe DefaultServlet.serveResource(), dort Aufruf von getOutputStream()
  this.getResponse().getOutputStream();
  return new CapturedServletOutputStream( this.buffer);
 }

 public PrintWriter getWriter() throws IOException {
  new Throwable("######## SCHREIBER").printStackTrace();
  this.getResponse().getWriter();
  return new PrintWriter(
    new OutputStreamWriter(
     new CapturedServletOutputStream( this.buffer)));
 }

 // http://marc.info/?l=tomcat-user&m=109913615025298
 public void flushBuffer() throws IOException {
  this.buffer.flush();
 }

 public String toString()     { return this.buffer.toString(); }
 public byte[] getByteArray() { return this.buffer.toString().getBytes(); }
 public char[] getCharArray() { return this.buffer.toString().toCharArray(); }
}

:: more /t1 src\milu\CapturedServletOutputStream.java
package milu;

import java.io.IOException;
import java.io.ByteArrayOutputStream;
import javax.servlet.ServletOutputStream;

public class CapturedServletOutputStream extends ServletOutputStream {

 ByteArrayOutputStream buffer; // Puffern, was geschrieben wird.

 CapturedServletOutputStream( ByteArrayOutputStream buf) {
  this.buffer = buf;
 }

 public void write( int b) throws IOException {
  this.buffer.write( b);
 }
 public void write( byte[] b) throws IOException {
  new Throwable().printStackTrace();
  this.buffer.write( b);
 }
 public void write( byte[] b, int off, int len) throws IOException {
  new Throwable().printStackTrace();
  this.buffer.write( b, off, len);
 }
 public void flush() throws IOException {
  this.buffer.flush();
 }
 public void close() throws IOException {
  this.buffer.close();
 }
}

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael,

Michael Ludwig wrote:
> Christopher Schultz schrieb am 12.11.2008 um 14:46:08 (-0500):
>> What you need to do is provide a unified buffer that /both/ calls can
>> write to. If you use a ByteArrayOutputStream at the lowest level and
>> then wrap that in an OutputStreamWriter for calls to getWriter(), you
>> should be able to handle arbitrary use of your wrapper.
> 
> This sounds very reasonable. I followed this road. Unfortunately, the
> included resource still is written elsewhere. Or maybe not flushed?

What do you mean, it's still written elsewhere?

>> Hope that helps,
> 
> Yes, this has helped. Thanks!
> 
> Anyway, what I would like to have is a trace of the method calls that
> are done by Tomcats's DefaultServlet. Is there a way to get one?

If you can wrap the object that's being called (like the request, or the
output stream/writer) then you can write your own logging statements at
appropriate times.

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkgvhQACgkQ9CaO5/Lv0PAC0ACgplWTifnCyqv7r69k5eGlxbeY
Ic0An2+VCCmCBVJGonpX50bRnGJ9N3fP
=Ay8r
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Michael Ludwig <mi...@gmx.de>.
Christopher Schultz schrieb am 12.11.2008 um 14:46:08 (-0500):
> Michael Ludwig wrote:
> > IllegalStateException:
> > 
> >   getOutputStream() has already been called for this response
> > 
> > But I haven't called getOutputStream() - I've called getWriter().
> 
> Yes, but your included resource probably called getOutputStream.

Yes, this is happening.

> You should really override getOutputStream as well as getWriter in
> your HttpServletResponseWrapper.

That's true. You have to, I think, in order to have your filter work in
all situations.

> > So how can I implement getOutputStream() to substitute a buffer?
> > Does anyone have an implementation?
> 
> How are you doing this with getWriter? With a StringWriter? Maybe you
> want to look at java.io.ByteArrayOutputStream.

For getWriter(), I was using PrintWriter with CharArrayOutputStream.

ByteArrayOutputStream as the most general stream implementation seems to
be the right thing to look at.

> What you need to do is provide a unified buffer that /both/ calls can
> write to. If you use a ByteArrayOutputStream at the lowest level and
> then wrap that in an OutputStreamWriter for calls to getWriter(), you
> should be able to handle arbitrary use of your wrapper.

This sounds very reasonable. I followed this road. Unfortunately, the
included resource still is written elsewhere. Or maybe not flushed?

> Hope that helps,

Yes, this has helped. Thanks!

Anyway, what I would like to have is a trace of the method calls that
are done by Tomcats's DefaultServlet. Is there a way to get one?

Michael Ludwig

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: [Http]ServletResponseWrapper.getOutputStream()

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael,

Michael Ludwig wrote:
> So the output of S gets collected by WR and is now available to F. And F
> calls getWriter() on the original response, and this to my surprise
> triggers an IllegalStateException:
> 
>   getOutputStream() has already been called for this response
> 
> But I haven't called getOutputStream() - I've called getWriter().

Yes, but your included resource probably called getOutputStream.

You should really override getOutputStream as well as getWriter in your
HttpServletResponseWrapper.

> So is it the recommended approach in this sort of scenario (or contrived
> example) to proceed as observed in the DefaultServlet, by first trying
> one of SOS and PW, catch the exception and then try the other one?

Unfortunately, I don't know if a way to check to see which of
getOutputStream/getWriter was called, so you might have to do this.

> So how can I implement getOutputStream() to substitute a buffer? Does
> anyone have an implementation?

How are you doing this with getWriter? With a StringWriter? Maybe you
want to look at java.io.ByteArrayOutputStream.

> But even with my Filter overriding both getOutputStream() and
> getWriter() and substituting buffers that will be written to, there is a
> problem. Servlet S will write to one buffer, and the DefaultServlet will
> write to another one. The place of the include set in the source code of
> S will be lost. Is this analysis correct?

Mostly. What you need to do is provide a unified buffer that /both/
calls can write to. If you use a ByteArrayOutputStream at the lowest
level and then wrap that in an OutputStreamWriter for calls to
getWriter(), you should be able to handle arbitrary use of your wrapper.

Hope that helps,
- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkbMoAACgkQ9CaO5/Lv0PCa9QCeMVLbd4dHVE6NwRHI9U2hAdM3
gxkAnR/zKPYvM8hQWSoKzGdrDoOsUC7q
=zT3d
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org