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

Reply via email to