GeekLondon.com Help icon Syndication Feed icon 

Context Filter

Update: There is a newer version of this filter packaged for more convenient usage.

When building a JEE web application, one of the things you have to look out for is the context path. If your application is going to deploy cleanly to multiple contexts (including the root), you may have problems.

The reason for this is pretty simple - http and html have no conception of a "root" other than the domain itself. They know about URIs, and neither of these standards really considers the possibility that the tree of content hanging from a particular domain might be partitioned into multiple sub-trees like JEE applications.

When you are creating a well-behaved JEE application you will probably want to deploy it to multiple contexts. During testing you may have multiple instances of the same application running on your application server - unless you plan to run each of these on a different port you'll need to deploy them to different contexts. Sometimes the customer will change their mind about where the application will be deployed - typically not even considering it until the last possible minute. And sometimes you'll be creating a shrink-wrapped application and forcing customers to accept an arbitrary context of your choosing is a pretty low trick. So how can you work around the problem?

Firstly, you can write all your URLs as relative URLs. Quite frankly, that sucks. If you're using the same template (a JSP or a velocity macro for example) to generate two different pages in the hierarchy, you start to have to do all sorts of shenanigans to get the template to work right. For example:

/mycontext/user/dcminter
/mycontext/admin/user/dcminter

Both of these URLs will be used to view essentially the same content, both could be rendered from a single JSP with appropriate caveats to hide or show administrative content. But relative links to the home context are different:

/mycontext/user/dcminter -> ../..
/mycontext/admin/user/dcminter -> ../../..

Get this wrong with clever URLs like this, and you can accidentally create links that cause the URL to expand interminably every time you click on them:

/mycontext/admin/admin/admin/admin/....

One work-around for this is to use the base element. For example, if you look at the page source for my blog, you'll see this:

<base href="http://geeklondon.com/blog/"/>

There are two things I dislike about this. Firstly, it makes *all* URLs relative to this path, so this makes URLs harder to create in circumstances where a relative URL would normally be no problem. Secondly, it requires access to the hostname, and there's no standard way to get the definitive hostname from a JEE application - so you have to provide it by some external means (typically JNDI parameters).

There's another issue that makes me slightly uncomfortable about this - the base element is supposed to be provided for the benefit of people creating mirrors of existing content. I've never seen anything that suggests that this is a suitable use of it. Even if it is, there's the discomfiting possibility that incautious mirroring software will fail to take account of the possibility that this is already being used when scraping my content.

So then there's a common solution - use the c:url standard tag to render URLs. The only problem I have with this is that it's hideous - I hate seeing XML-ish content inside XML/HTML properties. For example:

<input name="commit" src="<c:url value='/images/save.gif'/>" value="save" type="image"/>

If you think that's ok, look no further, but I think it's just ghastly.

This brings me to the solution that I currently favour: add a filter to the application that just stuffs the context path into the request parameter. This allows you to pull the context out on demand using Expression Language (EL). Here's what the above link monstrosity transforms into:

<input name="commit" src="${ctx}/images/save.gif" value="save" type="image"/>

Now I'll grant you that this is potentially more work than the use of the base element, but it does seem like a cleaner solution to me. I don't know if there are existing solutions to this problem, but a quick look around didn't find any. If you can see a practical disadvantage to the approach, let me know!

Here's my code for the filter:

package com.geeklondon.util;
 
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;
import javax.servlet.http.HttpServletRequest;
 
public class ContextFilter implements Filter {   
   public static final String ATTRIBUTE_CONFIG = "attribute-parameter";
   public static final String DEFAULT_CONTEXT_ATTRIBUTE = "ctx";
   private String ctxParam = DEFAULT_CONTEXT_ATTRIBUTE;
 
   public void destroy() {}
 
   public void doFilter(
            final ServletRequest request,
            final ServletResponse response,
            final FilterChain chain) 
      throws IOException, ServletException 
   {
      final HttpServletRequest rq = (HttpServletRequest)request;
      if( rq.getAttribute(ContextFilter.class.getName()) == null) {
         rq.setAttribute(ctxParam, rq.getContextPath());
         rq.setAttribute(ContextFilter.class.getName(), Boolean.TRUE);
      }
      chain.doFilter(request, response);
   }
 
   public void init(final FilterConfig config) 
      throws ServletException 
   {
      if( config.getInitParameter(ATTRIBUTE_CONFIG) != null ) {
         ctxParam = config.getInitParameter(ATTRIBUTE_CONFIG);
      }
   }
}

And here's the configuration fragment from a web.xml file:

<filter>
   <filter-name>contextFilter</filter-name>
   <filter-class>
      com.geeklondon.util.ContextFilter
   </filter-class>
</filter>

<filter-mapping>
   <filter-name>contextFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
Posted at Jul 14, 2007 8:07:05 PM, and last updated Nov 9, 2007 6:02:59 PM