views:

627

answers:

3

Writing generated PDF (ByteArrayOutputStream) in a Servlet to PrintWriter.

I am desperately looking for a way to write a generated PDF file to the response PrintWriter. Since a Filter up the hierarchy chain has already called response.getWriter() I can't get response.getOutputStream().

I do have a ByteArrayOutputStream where I generated the PDF into. Now all I need is a way to output the content of this ByteArrayOutputStream to the PrintWriter. If anyone could give me a helping hand would be very much appreciated!

+4  A: 

If something else has already called getWriter, it may well have already written some text to the response. Besides, PrintWriter is for text - you want to send arbitrary binary data... getOutputStream is definitely the way forward, so I would try to find the filter which has called getWriter and fix that instead.

Jon Skeet
Agree - anything else (trying to force sending binary data through a `PrintWriter`) is bound to become an ugly hack.
Jesper
Hm I see thanks for your help - it seams bit of a bad situation, cause I can't touch the filters before... any advise on a ugly hack?
Thomas
@Thomas: Not really... pushing binary data through a PrintWriter is like putting a square peg in a round hole. Why can you not change the filters? Is there really nothing you can do about them?
Jon Skeet
@Jon Thanks for the clear picture. Actually that was the solution proposed by the team that implemented the filter - since a bugfix seams to be in reach the whole situation is more relaxed now.Thanks again for your help, brds T
Thomas
A: 

Do you know what's the encoding of PrintWriter? If it's Latin-1, you can simply convert the byte array to Latin-1 and write to the PrintWriter,

   String latin1 = byteStream.toString("ISO-8859-1");
   response.getWriter().write(latin1);

Of course, this assumes the filter doesn't really write anything.

ZZ Coder
Not correct, he should be using response.getOutputStream().
Christoffer Hammarström
@Christoffer: Correct only that will throw an exception since response.getWriter() was called already beforehand.
Thomas
@Thomas: The question then becomes why something has called getWriter() and not written anything. That does not make sense and at the very least sounds like a bug waiting to happen.
Christoffer Hammarström
A: 

Here's a somewhat crazy idea, but i would probably solve it like this.

If you really can't touch the broken Filter (really?), write another filter that you place before the broken Filter.

This looks more complex than it is, but that's only because of Java's verbosity, so please bear with me.

Basically what it does is that it uses HttpServletResponseWrapper to wrap/"override" response.getWriter() in filters and the servlet following it in the chain.

So when your broken Filter calls response.getWriter(), it will instead get a proxy PrintWriter that only calls the real response.getWriter() the first time it is actually written to.

Then it no longer matters if the broken Filter calls response.getWriter() without writing to it.

I haven't actually tested this code, but i believe it should work.

Note that this assumes that the broken Filter calls response.getWriter() without actually writing something. The output would be corrupt anyway if the broken filter wrote something and then you tried to write a PDF to it as well.

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;

public class BrokenWriterGetterFixingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, final ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        filterChain.doFilter(servletRequest, new BrokenWriterGetterFixingResponseWrapper((HttpServletResponse) servletResponse));
    }

    @Override
    public void destroy() {
    }

    private static class BrokenWriterGetterFixingResponseWrapper extends HttpServletResponseWrapper {
        public BrokenWriterGetterFixingResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(new BrokenWriterGetterFixingWriter(getResponse()));
        }
    }

    private static class BrokenWriterGetterFixingWriter extends Writer {
        private PrintWriter responseWriter;
        private final ServletResponse response;

        public BrokenWriterGetterFixingWriter(ServletResponse response) {
            this.response = response;
        }

        @Override
        public void write(char[] cbuf, int off, int len) throws IOException {
            getResponseWriter().write(cbuf, off, len);
        }

        @Override
        public void flush() throws IOException {
            getResponseWriter().flush();
        }

        @Override
        public void close() throws IOException {
            getResponseWriter().close();
        }

        private PrintWriter getResponseWriter() throws IOException {
            if (null == responseWriter) {
                responseWriter = response.getWriter();
            }
            return responseWriter;
        }

    }
}
Christoffer Hammarström