Intercepting HTTP Response using Servlet Filter

March 16, 2010

Hello,
Servlet Filters are used to intercept the HTTP Requests/HTTP Responses.
Filters generally contain some logic that can be applied across a URL pattern.
I have used filters generally to verify if user is logged in or if user has appropriate privilege to access the resource.
While addressing such requirements we end up writing a Filter that is better known as Request Filter because it
intercepts the HTTP request. So most of the time when I ask someone if they know how to implement a HTTP Response Filter, they look puzzled. Of course,I too have gone through this situation in past.

Below is the sample code for Filter(Request Filter)  –

package com.amit.web.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter implements Filter {

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// place your code here
 // pass the request along the filter chain
chain.doFilter(request, response);
}

public void init(FilterConfig arg0) throws ServletException {}
public void destroy() {}
}

You would know that the line “chain.doFilter(request,response)“, is responsible for executing the next Filter or web resource in line.
Hence if I map MyFilter for servlet named MyServlet then any request for MyServlet would be intercepted by MyFilter. When it encounters “chain.doFilter(request,response)“,it would call the MyServlet’s service method.
Once MyServlet has finished its task, the call to “doFilter” would return and if there is no code/logic written after this line, HTTP Response goes back to the client.
This precisely is the location where we can write the code that would alter the HTTP Response.
This means if we need to write a HTTP Response filter i.e. intercepting HTTP Response, then we need to write the business logic after “chain.doFilter(request,response)” and we are intercepting the HTTP response before it reaches the destination.  The method doFilter that would serve as HTTP Response filter is shown below –

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
//PLACE YOUR CODE HERE THAT MODIFIES THE RESPONSE.
} 

Overall this is preety easy but usually goes unnoticed which is the reason I felt writing about this.

Another important point about Servlet Filters is –
Consider there are MyFilter1, MyFilter2 and MyFilter3 configured for any requests to Servlet MyServlet.
In this case MyFilter1, MyFilter2 and MyFilter3 will be executed in the order of configuration while intercepting the HTTP Requests.
While sending back the response they would be executed in the REVERSE order as shown in the diagram below.

Filter-Invocation-Sequence

Hope this helpes someone who is wondering how to implement HTTP Response Filters.Cheers !!
Amit


Decoupling Java applications and config/properties files

March 11, 2010

Hello,
I have been developing a stand-alone Java application packaged as an executable JAR file.
The application had,

  1. Database interaction using iBatis APIs.
  2. Log the various activities in a log file using log4j API.

I had packaged the “my-db.properties” file that feeds the driver/username/password information to “SqlMapConfig.xml(iBatis configuration file)” within the JAR itself.Also the “log4j-myapp.properties” file was placed inside the JAR.

This introduced strong coupling/dependency between these files and JAR. Whenever I need to change database or log4j configuration, I had to re-package the JAR.

I was searching for a solution for this and based on google searches and interpretation of Java documentation for Class.getResourceAsStream() addressed this problem in the following way.

Solution:

  1. Consider that we are generating this executable jar (myapp.jar) in  “myapp/dist” directory.
  2. To resolve dependencies on iBatis and log4j, put respective JARs in the same i.e “myapp/dist” directory.
  3. Add Class-Path attribute in “Manifest.mf” file that lists these JAR files.
  4. This can be done by providing the custom manifest file while generating the JAR file. #2 and #3 are widely used in various enterprise as well as standalone applications so doing this is not an issue.

  5. Create a directory named “config” in “myapp/dist” where we will keep properties file. Of course this can be any name you like.
  6. Add the entry “config/” to the Class-Path attribute in “Manifest.mf” file. This brings “config” directory in the classpath while executing the application.
  7. Put the “log4j-myapp.properties” and “my-db.properties” in “config” directory.
  8. Your application(may be constructor of your main class) can load the log4j configuration as shown below –
  9. 	/*Initialize log4j */
    	Properties props = new Properties();
    	props.load(this.getClass().getResourceAsStream("/log4j-myapp.properties"));
    	props.list(System.out);
    	PropertyConfigurator.configure(props);
    
  10. SqlMapConfig.xml” can point to the resource properties file as shown below. This does not change much as compared to log4j.
  11. <sqlMapConfig>
       <!-- This is where we specify the properties file where DB configuration is specified -->
       <properties resource="my-db.properties"/>
       <transactionManager type="JDBC" commitRequired="false">
       <dataSource type="SIMPLE">
         <property name="JDBC.Driver" value="${driver}"/>
         <property name="JDBC.ConnectionURL" value="${url}"/>
         <property name="JDBC.Username" value="${username}"/>
         <property name="JDBC.Password" value="${password}"/>
       </dataSource>
      </transactionManager>
      <sqlMap resource="com/amit/test/xyz.xml"/>
    </sqlMapConfig>
    
    
  12. Now when you run your application (java -jar myapp.jar), the properties files will be picked up from the config directory.

This ensures you do not need to change the JAR file for making Database and log4j configuration changes.

The Directory structure for “dist” looks as shown below –

Directory Strcture for "dist": myapp |----dist | |----myapp.jar | |----ibatis-2.3.4.726.jar |----log4j-1.2.14.jar |----ojdbc14_g.jar |----config | |---log4j-myapp.properties |---my-db.properties

Manifest.mf classpath entry as below –

Class-Path: config/ log4j-1.2.14.jar ibatis-2.3.4.726.jar ojdbc14_g.jar

Why this works?
If you see the line that loads log4j configuration file, it has “/” at the start of filename.

props.load(this.getClass().getResourceAsStream("/log4j-myapp.properties"));

As per the Java documentation(see below) for the method, we have to give “/” at the start and then only the resource will be located using absolute name calculated as mentioned.

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

I was facing issues earlier because,

  1. I was only giving “log4j-myapp.properties” while loading the log4j configuration in my program.
  2. In the Manifest.mf, I was not appending “/” at the end of “config” directory. Without this the resource was not getting located.

Of course this is not just limited to iBatis and/or log4j, but any properties file that needs to be controlled outside the realm of the JAR file especially in stand-alone application can be configured this way.
Java API documentation to the rescue once again. Many thanks to them.

Cheers !!
Amit


Webapp generated Excel Spreadsheet opens in same browser window – IE6

March 8, 2010

Hello,

While working on a web application that generates Excel spreadsheets , I faced the issue mentioned below.

Issue:

Sometimes the spreadsheet is opened up in a new MS-Excel application window and often opened in the same browser window.  This is used to happen for Internet Explorer 6.0. On IE7 it was not reproducible i.e. used to open up in the MS-Excel application all the time leaving browser intact.

Additional Information:

I had implemented this spreadsheet generation as below –

  1. Generate the spreadsheet using Apache POI.
  2. Set header content-type to “application/vnd.ms-excel“.
  3. In addition to content-type, I was also adding the header “Content-disposition” that assigns a filename to the content written on the response stream. Some excel files were named like “xyz/abc.xls“.
  4. Write the bytes on the Response stream.

Resolution:

We could figure out the behavior because when we ran the application and try to retrieve the report,

  1. For spreadsheets with filenames that did not contain “/” displayed the browser File Download dialog properly with “filename.xls”.
      ie6-saveas-noslash
  2. Whereas if the filename contained “/” in it then the File Download dialog shows
  • EITHER the URL that is used to retrieve the excel report from server. e.g. if you have a ReportServlet that serves the excel report then it would show “ReportServlet” instead of filename.
  • OR some random filename as shown below

ie6-filedownload

Once we fixed the filename generation logic to replace “/” with “-“(or any other valid character), reports started opening up in MS-Excel application than in same browser window.

Another thing to note here is that IE7 handles it internally by replacing “/” with “_” as shown below.

IE7-File Download Dialog

Note:

In addition to this while searching I found out the Microsoft knowledge-base which mentioned this issue can be resolved by doing some client side settings. This knowledge-base can be found at KB-162059.

Cheers !

Amit


Dependency Injection and Using Struts Actions as Spring Beans

March 1, 2010

Hello,

I had created this presentation earlier to mentor my team members about Spring Dependency Injection fundamentals and explaining approach to integrate Struts 1.3 Action classes and Spring framework. Approach I took was using “DelegatingActionProxy” for using Struts action classes as Spring beans.

Introduction to Spring DI and Using Struts Actions as Spring Beans

Introduction to Spring - First page

 

I should have posted it then itself but then sooner is better than never. Hope that you find this useful.

Cheers !

Amit