Modifying and Uncommitting Java Filter Response

Aim
The aim of this tutorial is to be able to stop a response from being committed even after doFilter is called so we can redirect the response depending on an error. Strictly speaking you cannot uncommit a response. All you can do is prevent it from getting committed. For example if there is a 404 Error, this allows you to detect this error and change the html text or redirect the response without causing the dreaded IllegalStateException? Please leave any question or comments at the end and I will endeavour to answer them.

Assumptions
This article assumes that you have Glassfish or Tomcat or an equivalent java container installed and configured.

Versions used in this example
Sofware/Component
Image
Windows XP SP2
N/A
Glassfish
N/A
JDK 1.5.0
N/A
Links to these files can be found here

For the purposes of this tutorial the files are stored in the structure below. The FilterModifyResponse directory is the working directory.

FilterExample
_|_web_bin
_|__|_index.html
_|__|_WEB-INF
_|__|__|_web.xml
_|__|__|_classes
_|__|_filtered
_|__|__|_index.html
_|_web_src
_|__|_myfilter
_|__|__|_MyFilter.java
_|__|__|_FilterServletResponseWrapper.java

Create and deploy the Service
  1. Write the Response Wrapper that will prevent the Errors from being Committed and will also allow us to extract the error. Save this file as FilterServletResponseWrapper under the web_src/myfilter directory. As you can see the sendError and flushBuffer functions do not call the parent functions. We save the error code as httpStatus and this can be got at anytime using getStatus.
     1. package myfilter;
     2. 
     3. import javax.servlet.http.HttpServletResponse;
     4. import javax.servlet.http.HttpServletResponseWrapper;
     5. 
     6. import java.io.*;
     7. 
     8. public class FilterServletResponseWrapper extends HttpServletResponseWrapper{
     9. 
    10.     private int httpStatus;
    11.     
    12.     public FilterServletResponseWrapper(HttpServletResponse response){
    13.         super(response);
    14.         httpStatus = HttpServletResponseWrapper.SC_OK;
    15.     }
    16. 
    17.     public void sendError(int sc) throws IOException {
    18.         //super.sendError(sc);
    19.         httpStatus = sc;
    20.     }
    21.     public void sendErrorEx(int sc) throws IOException {
    22.         httpStatus = sc;
    23.         super.sendError(sc);
    24.     }
    25.     
    26.     
    27.     public void setStatus(int sc){
    28.         super.setStatus(sc);
    29.         httpStatus = sc;
    30.     }
    31.     public int getStatus(){
    32.         return httpStatus;
    33.     }
    34.     
    35.     public void flushBuffer(){
    36.         //super.flushBuffer();
    37.     }
    38.     public void flushBufferEx() throws IOException {
    39.         super.flushBuffer();
    40.     }
    41. }
    Hide line numbers

  2. Write the Filter and save it as MyFilter.java in the web_src/myfilter directory. As you can see, it tests the http status by calling getStatus(). If there is an error then we redirect
     1. package myfilter;
     2. 
     3. import java.io.IOException;
     4. import javax.servlet.*;
     5. import javax.servlet.http.HttpServletRequest;
     6. import javax.servlet.http.HttpServletResponse;
     7. import javax.servlet.http.HttpServletResponseWrapper;
     8. import javax.servlet.http.HttpSession;
     9. 
    10. import java.util.logging.*;
    11. 
    12. 
    13. public class MyFilter implements javax.servlet.Filter {
    14.     private ServletContext servletContext;
    15.     private Logger log;
    16.     
    17.     public MyFilter(){
    18.         super();
    19.     }
    20.     
    21.     public void init(FilterConfig filterConfig) throws ServletException {
    22.         servletContext = filterConfig.getServletContext();
    23.         log = Logger.getLogger(MyFilter.class.getName());
    24.     }
    25. 
    26.     public void doFilter(   ServletRequest req, 
    27.                             ServletResponse res, 
    28.                             FilterChain filterChain)
    29.         throws IOException, ServletException {
    30. 
    31.         HttpServletRequest httpReq    = (HttpServletRequest)req;
    32.         FilterServletResponseWrapper httpRes   = 
    33.                                         new FilterServletResponseWrapper((HttpServletResponse)res);
    34.         
    35.         filterChain.doFilter(httpReq, httpRes);
    36.         if(httpRes.getStatus() !=  HttpServletResponseWrapper.SC_OK)
    37.             httpRes.sendRedirect("http://www.google.com");
    38.             
    39.     }
    40. 
    41.     public void destroy(){
    42.     }
    43. 
    44. }
    Hide line numbers

  3. Now cd into the web_src directory and compile the source. Replace the path to Glassfish's lib directory with your local path. This could be lib in tomcat or any servlet container.

    ..FilterModifyResponse\web_src>javac myfilter\MyFilter.java -d ..\web_bin\WEB-INF\classes -extdirs d:\downloads\glassfish\lib

  4. Now create the web.xml file which will have our filter mapping. Save this under the WEB-INF directory under web_bin. Ignore the init parameters as this is not used in this example.
     1. <?xml version="1.0" encoding="ISO-8859-1"?>
     2. 
     3. <web-app>
     4. 
     5.    <!-- Define servlet-mapped and path-mapped example filters -->
     6.     <filter>
     7.         <filter-name>My Filter</filter-name>
     8.         <filter-class>myfilter.MyFilter</filter-class>
     9.         <init-param>
    10.             <param-name>Redirect-Page</param-name>
    11.             <param-value>http://www.google.com</param-value>
    12.         </init-param>
    13.     </filter>
    14. 
    15.     <!-- Define filter mappings for the defined filters -->
    16.     <filter-mapping>
    17.         <filter-name>My Filter</filter-name>
    18.         <url-pattern>/filtered/*</url-pattern>
    19.     </filter-mapping>
    20. 
    21. </web-app>
    Hide line numbers
    As you can see the filter will intercept any url starting with "/filter/*"

  5. now create a simple index.html file and save one copy in the web_bin directory and one in the web_bin/filtered directory.
     1. <html>
     2. <head></head>
     3. <body>
     4. <h1>Test</h1>
     5. </body>
     6. </html>
    Hide line numbers

  6. Now create the war file. Cd into the web_bin directory and jar it all up.

    ..FilterModifyResponse\web_bin>jar -cvf myfilter.war *

  7. Finally deploy this war file in the servlet container of your choice. Glassfish in this example.

Testing the filter
  1. First navigate to
    http://localhost:8080/myfilter/filtered/index.html.
    No change as the html is just served up.

  2. Now navigate to
    http://localhost:8080/myfilter/filtered/index2.html.
    Instead of getting the standard 404 file not found error, you will be redirected to http://www.google.com.


To add headers to a Request please visit Adding HTTP Headers To Requests in Filters and Servlets.