The majority of my most recent projects have revolved around Java on Tomcat. Tomcat is a nice Java server but lacks some of the features associated with the larger containers. One such feature is the inability for Tomcat to set Far Future Expires Headers natively. This is because there is usually an Apache HTTP web server sitting on front of it. As with any configuration when Apache HTTP is used as a web proxy this is not possible and that is where a Filter would come in. I have outlined the code for the HeaderFilter below and the subsequent web.xml changes.
package net.tomred.filters;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* @author Dermot Butterfield <support[@]tomred.net>
*/
public class HeaderFilter implements Filter {
private static final Logger logger = Logger.getLogger(HeaderFilter.class.toString());
private FilterConfig filterConfig;
private Map<String, String> headersMap;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
String headerParam = filterConfig.getInitParameter("header");
if (headerParam == null) {
logger.info("No headers were found in the web.xml (init-param) for the HeaderFilter !");
return;
}
// Init the header list :
headersMap = new LinkedHashMap<String, String>();
if (headerParam.contains("|")) {
String[] headers = headerParam.split("|");
for (String header : headers) {
parseHeader(header);
}
} else {
parseHeader(headerParam);
}
logger.info("The following headers were registered in the HeaderFilter :");
Set<Entry<String, String>> headers = headersMap.entrySet();
for (Entry<String, String> item : headers) {
logger.info(item.getKey() + ':' + item.getValue());
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (headersMap != null) {
// Add the header to the response
Set<Entry<String, String>> headers = headersMap.entrySet();
for (Entry<String, String> header : headers) {
((HttpServletResponse) response).setHeader(header.getKey(), header.getValue());
}
}
// Continue
chain.doFilter(request, response);
}
public void destroy() {
this.filterConfig = null;
this.headersMap = null;
}
private void parseHeader(String header) {
String headerName = header.substring(0, header.indexOf(":"));
if (!headersMap.containsKey(headerName)) {
headersMap.put(headerName, header.substring(header.indexOf(":") + 1));
}
}
}
I am sure this is pretty self-explanatory using the init() we identify if there is any header params set in the web.xml we then parse the params and retrieve the existing headers. At this point doFilter() is called and the HttpServletResponse is re-created using the new values. The destroy() is then called to clean up and the filter is finished.
As stated above the init() expects to find header params in the web.xml I have outlined how this should be displayed below. The following settings set the Far Future Headers for *.css and *.png you can of course do this for any extension you might be serving but bear in mind that if this content naming is not versioned then it will not be reloaded if loaded by a browser until the expire header is reached.
<web-app ...> <filter> <description>Set HTTP headers for a mapping.</description> <filter-name>HeaderFilter</filter-name> <filter-class>net.tomred.filters.HeaderFilter</filter-class> <init-param> <description>Add an Expires Header</description> <param-name>header</param-name> <param-value>Expires: Wed, 19 May 2010 00:00:00 GMT</param-value> </init-param> </filter> <filter-mapping> <filter-name>HeaderFilter</filter-name> <url-pattern>*.css</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> <filter-mapping> <filter-name>HeaderFilter</filter-name> <url-pattern>*.png</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> </web-app>
I hope you have found this useful if you have any questions leave a comment or email support[@]tomred.net


