-----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]