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();
    }
}
 

Wrapping an HttpServletRequest and HttpServletResponse


Sometimes we want to wrap our request and response objects and provide 
new functionality as seen in the classes below. Below is an example of 
how to wrap these objects. There needs to be a filter that intercepts 
the request and response and supply the wrapped objects. The filter needs
to be mapped in web.xml which is not shown. The code below uses another 
class I explained in another post (link here). 
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class CustomHttpServletRequestWrapper extends 
                                    HttpServletRequestWrapper {

 private final Map parameterMap;
 private static final String caller = 
                             "CustomHttpServletRequestWrapper";
 private String method;
 
 public CustomHttpServletRequestWrapper(HttpServletRequest request) {
 super(request);

 this.parameterMap = copyParameters(request);
 this.method = request.getMethod();
 }

 @SuppressWarnings("unchecked")
 private Map copyParameters(HttpServletRequest request) {
 final Map result = new HashMap();
 result.putAll(request.getParameterMap());

 return result;
 }

 @Override
 public String getMethod() {
 return method;
 }

 @Override
 public String getParameter(String name) {
 String[] values = getParameterValues(name);
 return (values != null && values.length > 0) ? values[0] : null;
 }

 @Override
 public Map getParameterMap() {
 return parameterMap;
 }

 @Override
 public Enumeration getParameterNames() {
 return Collections.enumeration(parameterMap.keySet());
 }

 @Override
 public String[] getParameterValues(String name) {
 return parameterMap.get(name);
 }

 public void setMethod(String method, String reason) {
 if (this.method != method) {
  final StringBuilder sb = new StringBuilder();

 sb.append(" Method changed to ").append(method);
 sb.append("\n  ").append(reason);

 ThreadLogger.message(caller, sb.toString());

 this.method = method;
 }
 }

 public void renameParameter(String oldName, String newName) {
 String[] values = parameterMap.get(oldName);
 if (values != null) {
  parameterMap.remove(oldName);
  parameterMap.put(newName, values);
 }
 }

 public void removeParameter(String name) {
 parameterMap.remove(name);
 }
}

import java.io.IOException;

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";

 public CustomHttpServletResponseWrapper(HttpServletResponse response) {
 super(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();
 }
 }

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomFilter implements javax.servlet.Filter {

 private String dummyInitParam;

 private static final String LOGGER_PREFIX = "[CustomFilter] ";
 private static final String caller = "CustomFilter";

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

 this.dummyInitParam = filterConfig
   .getInitParameter("existingPlusLoginUrl");

 }

 @Override
 public void destroy() {
 ThreadLogger.message(caller, LOGGER_PREFIX + "destroyed");
 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
  FilterChain chain) throws IOException, ServletException {

 final CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper(
   (HttpServletRequest) request);
 final CustomHttpServletResponseWrapper responseWrapper = new CustomHttpServletResponseWrapper(
   (HttpServletResponse) response);

 try {
  doHttpFilter(requestWrapper, responseWrapper, chain);
 }

 finally {
  responseWrapper.completed(); // need to call this to flush
          // ThreadLogger
 }
 }

 public void doHttpFilter(CustomHttpServletRequestWrapper request,
  CustomHttpServletResponseWrapper response, FilterChain chain)
  throws IOException, ServletException {
 ThreadLogger.message(caller, "dummyParam: " + dummyInitParam);

 // log important stuff
 logStartOfRequest(request);

 // do other stuff you want to do here.

 // turn off caching
 setNoCacheHeaders(response);

 executeChain(request, response, chain);
 }

 private void executeChain(CustomHttpServletRequestWrapper request,
  HttpServletResponse response, FilterChain chain)
  throws IOException, ServletException {
 ThreadLogger.message(caller, "Executing filter chain");

 chain.doFilter(request, response);

 }

 private void setNoCacheHeaders(HttpServletResponse response) {
 response.setHeader("Expires", "Tue, 31 Dec 2010 00:00:00 UTC");
 response.setDateHeader("Last-Modified", System.currentTimeMillis());
 response.setHeader("Cache-Control",
   "no-store, no-cache, must-revalidate, max-age=0");
 response.setHeader("Pragma", "no-cache");
 }

 private void logStartOfRequest(CustomHttpServletRequestWrapper request) {
 // This must be called to enableDebug mode if it is set. Set a cookie
 // enableDebug = true to enable debug.
 // This will log all your request's state
 ThreadLogger.logStartOfRequest(caller, request);
 }
}

Coding - A generic String to Date parse utility


A generic Date utility that accepts date in ymd or dmy format. 
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateUtils {
 private static final Pattern ymd = Pattern
 .compile("^(\\d{4})-([1-9]|[0][1-9]|1[0-2])
              -([1-9]|0[1-9]|[12][0-9]|[3][01])$");
 private static final Pattern dmy = Pattern
 .compile("^([1-9]|[0][1-9]|1[0-2])
              -([1-9]|0[1-9]|[12][0-9]|[3][01])-(\\d{4})$");
 private static final DateFormat df = new SimpleDateFormat(
                                                  "yyyy-MM-dd");
 
 static {
 //prevents using "2001-02-29" for March 1st
 df.setLenient(false);
 }
 
 private static Date parse(String value) {
  if (value == null) {
 return null;
  }
 
 String input = value.trim().replaceAll("[\\./ ]", "-");
 if (input.length() < 8) {
  return null;
 }
 
 String ds = null;
 Matcher matcher = ymd.matcher(input);
 
 if (matcher.find()) {
  ds = matcher.group(1) + "-" + matcher.group(2) + "-"
   + matcher.group(3);
 } else {
  matcher = dmy.matcher(input);
  if (matcher.find()) {
   ds = matcher.group(3) + "-" + matcher.group(1) + "-"
    + matcher.group(2);
  }
 }

 Date result = null;
 if (ds == null) {
 // user's inputer does not match an expected format
 } else {
 try {
  result = df.parse(ds);
} catch (ParseException e1) {
 // should not happen, but treat it the same as invalid input
 // there is no need to log
 }
}

 return result;
}

public static void main(String[] args) {
 String[] inputs = { "2016-9-8", "2016-09-8", "2016-09-08", "2016-9-08",
  "9-8-2016", "09-8-2016", "09-08-2016", "9-08-2016", "9/8/2016",
  " 9-8-2016", " 2016-9-8", "9-8-2016 ", "2016-9-8 ",
  "2016 9 8 ",
  null, "13-1-9999", "1-33-9999", "9999-13-1", "9999-1-33" };

 for (String input : inputs) {
 System.err.println(input + "\t" + parse(input));
 }
}

}


Coding patterns - coding a method that always throws an exception


 
Sometimes we have a method that does some business logic and it 
always throws a known exception. One example can be if a user tried
to login unsuccessfully, we want to log that information and 
always throw a custom exception which the clients catch and handle
accordingly. In such case there is a simple coding pattern that 
makes the code look cleaner. 
This is an example without the coding pattern applied. See below with 
the pattern.

public void  handleLoginFailure(String CustomExceptionMessage) 
                                         throws CustomException{
 //do some logic before throwing the CustomException
 throw new CustomException(CustomExceptionMessage);
}

public void testMethod() throws CustomException{
 handleLoginFailure("username and password invalid"); 
}

This is an example with the coding pattern applied. Now someone 
reading testMethod() knows that handleLoginFailure() always throws 
an exception thus simplyfing the code readability.
public CustomException handleLoginFailure
                         (String CustomExceptionMessage) {
 //do some logic before throwing the CustomException
 return new CustomException(CustomExceptionMessage);
}

public void testMethod() throws CustomException{
 throw handleLoginFailure("username and password invalid"); 
}

Openam custom modules - use of redirect callback


<ModuleProperties moduleName="SampleAuth" version="1.0" >
    <Callbacks length="1" order="1" timeout="600" header="Redirecting" >
        <RedirectCallback method="get">
            <RedirectUrl>#Dummy url#</RedirectUrl>
            <RedirectStatusParam>#Dummy url#</RedirectStatusParam>
        </RedirectCallback>        
        <NameCallback isRequired="true">
            <Prompt>return parameter from redirected url</Prompt>
        </NameCallback>
    </Callbacks>
</ModuleProperties>



in your process method you will have this code returning 1 (which is order of  the callbacks tag which has RedirectCallback)





Map<String, String> redirectData = new HashMap<String, String>();
redirectData.put("param1", "123");
redirectData.put("param2", "abc");


RedirectCallback rc = new RedirectCallback(redirectUrl, redirectData, "POST");
super.replaceCallback(1, 0, rc); // 1 is order of callbacks tag. 0 is the first callback which is                //RedirectCallback





Openam custom modules - stop chaing processing and take the user to error page


protected LoginException handleLoginFailure(String url, String exceptionMessage) {
        try {
            if(url != null){
                super.setLoginFailureURL(url);
            }
        } catch (AuthLoginException e) {
            ThreadLogger.error(moduleName, "Failed to set the loginFailureUrl", e);
        }

        ThreadLogger.message(moduleName, "handleLoginFailure about to throw Exception");

        return new LoginException(exceptionMessage);
    }

Openam custom modules - Role of LOGIN_IGNORE