Saturday, November 9, 2013

Handling "Response already committed" IllegalStateException


"Response already committed" is an exception that we quite often 
see when we are using several mvc frameworks. This is typically seen 
when we try to redirect the user to a different resource based on some 
Exception. A simple solution to this would be to cache the response until
we are ready to commit the response. Below solution uses 
ByteArrayOutputStream to cache the response and commit it when the final 
response is ready.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class CustomHttpServletResponseWrapper extends
        HttpServletResponseWrapper {

    private static final String caller = "CustomHttpServletResponseWrapper";
    private BufferedServletOutputStream stream;
    
    private final class BufferedServletOutputStream extends ServletOutputStream {
        private final ByteArrayOutputStream out = new ByteArrayOutputStream();
        private final HttpServletResponse response;

        private BufferedServletOutputStream(HttpServletResponse response) {
            this.response = response;
        }
        
        /**
         * Send the cached response to the device.
         */
        public void commit() throws IOException {
            response.getOutputStream().write(out.toByteArray());
        }

        public void write(int b) throws IOException {
            out.write(b);
        }

        /**
         * Forget the current response; start over again.
         */
        public void reset() {
            out.reset();
        }        
    }
    


    public CustomHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
        this.stream = new BufferedServletOutputStream(response);
    }

    @Override
    public void addCookie(Cookie cookie) {

        final StringBuilder buffer = new StringBuilder();

        final String cookieName = cookie.getName();
        final String cookieValue = cookie.getValue();

        super.addCookie(cookie);

        // generate a debug message indicating the addition of cookie
        ThreadLogger.message(caller, " Set cookie[" + cookieName + "] = "
                + cookieValue);
        if (cookie.getSecure()) {
            buffer.append("\n  Secure");
        }
    }

    @Override
    public void sendError(int sc) throws IOException {
        if (sc >= HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
            ThreadLogger.error(caller, "Sending error response " + sc);
        } else {
            ThreadLogger.warn(caller, " Sending error response " + sc);
        }
        super.sendError(sc);
    }

    @Override
    public void sendError(int sc, String msg) throws IOException {
        if (sc >= HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
            ThreadLogger.error(caller, "Sending error response " + sc + "\n  "
                    + msg);
        } else {
            ThreadLogger.warn(caller, " Sending error response " + sc + "\n  "
                    + msg);
        }
        super.sendError(sc, msg);
    }

    @Override
    public void sendRedirect(String location) throws IOException {
        ThreadLogger.message(caller, " Sending redirect to " + location);
        super.sendRedirect(location);
    }

    @Override
    public void setHeader(String name, String value) {
        super.setHeader(name, value);

    }

    public void completed() {
        ThreadLogger.message(caller, "Response completed.");
        ThreadLogger.flush();
    }
    
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return stream;
    }

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

    @Override
    public void reset() {
        stream.reset();
        ThreadLogger.message(caller, "Cleared previous response.");

        super.reset();
    }

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

No comments:

Post a Comment