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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to