Wednesday, July 15, 2009

Java EE: Filters. An example: reading HTTP headers.

You would not need to read HTTP headers so frequently during the development of your Java EE web modules. You aren't probably writing servlets any longer. Nevertheless, it might happen one day that you need to explore what's there, inside the HTTP headers that go to your servlet or your JSP. That's easy enough, though: just use the getHeader method (and its brothers) of the HttpServletRequest interface.

But if you're using HTTP headers you're not moving information to be used by the business logic. At least, I hope you don't. That's why you might also be interested in not polluting your business logic (servlets' code, JSPs' and so on). The solution to such problems may be a filter, as specified in the Java Servlet Specification, chapter SRV.6.

What's a filter?

Good question, that's why the servlet specification is there! Filters interpose during the call to a web resource between the client and the resource itself and the container executes them before the resource is invoked. With a filter you could, for example:
  • Intercept a call to a web resource to perform logic which is orthogonal to the code into the resource itself.
  • Modify the request content before it gets accessed by the web resource.
  • Deny access to the resource, by redirecting the user to another resource or by launching an exception.
The Servlet Specification is the place where you'll find all the details about filters. Here I'll just write down a little example so that you know what a filter is and how to use it.


As far as it concerns the code, a filter is a class which implements the javax.servlet.Filter interface. There clearly are two life-cycle management methods:
  • init(FilterConfig fc)
  • destroy()
The third method of the interface is where filter logic will be executed:
  • doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
So, let's write the body of our shiny little filter. That's pretty easy with NetBeans: it has templates for web module filters.

Coding the filter


Coding the init and destroy methods.

Coding the init and the destroy methods is straightforward, unless you really put logic in them, such as custom filter configuration, external resources initialization and so on. A basic implementation for these method is the following:

private FilterConfig filterConfig = null;
private static final boolean debug = false;

public void destroy() {
}

public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
if (filterConfig != null) {
if (debug) {
// the log method is implemented elsewhere
// and relies on the ServletContext instance
// to log its messages.
log("DPErrorFilter:Initializing filter");
}
}
}

Coding the filter logic

That's pretty straightforward, too. Maybe the only thing you should remember is just... invoking the other filters in the chain. That's why the method signature has that FilterChain in it. This is NetBeans' doFilter prototype

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

if (debug) {
log("DPErrorFilter:doFilter()");
}

doBeforeProcessing(request, response);

Throwable problem = null;
try {
chain.doFilter(request, response);
} catch (Throwable t) {
// If an exception is thrown somewhere down the filter chain,
// we still want to execute our after processing, and then
// rethrow the problem after that.
problem = t;
t.printStackTrace();
}

doAfterProcessing(request, response);

// If there was a problem, we want to rethrow it if it is
// a known type, otherwise log it.
if (problem != null) {
if (problem instanceof ServletException) {
throw (ServletException) problem;
}
if (problem instanceof IOException) {
throw (IOException) problem;
}
sendProcessingError(problem, response);
}
}

Reading HTTP headers

The last piece of code you miss is the logic itself. Here it is:
private void doBeforeProcessing(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (debug) {
log("DPErrorFilter:DoBeforeProcessing");
}

// wrapping the servlet request
if (!(request instanceof HttpServletRequest)) {
log("Request isn't an instance of HttpServletRequest, skipping...");
return;
}

HttpServletRequestWrapper req =
new HttpServletRequestWrapper((HttpServletRequest) request);

// read your headers here
}

The only trick here is wrapping your ServletRequest (which lacks HTTP-related functionality) parameter into an HttpServletWrapper. I also do an instanceof check before doing it, just in case, and return if the object type isn't the expected.

Using the filter

To use the filter, you have to deploy it and configure your deployment descriptor, the web.xml file. The required steps are:
  • Declare the filter.
  • Map the filter.
The filter declaration is a typical declaration: you define a name for your filter class. The filter mapping defines when the filter execution is triggered; you can specify an URL pattern and an optional dispatcher type. The dispatcher type also specifies when the filter has to be triggered, depending on the request type being executed:
  • REQUEST (the default value)
  • FORWARD
  • INCLUDE
  • ERROR
Here's a typical XML fragment from web.xml:
<filter>
<filter-name>DPErrorFilter</filter-name>
<filter-class>
es.trafico.datapower.exceptions.filters.DPErrorFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>DPErrorFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

In this case the filter is triggered at every request because of the /* URL pattern.

No comments: