Thursday, May 21, 2009

Url patterns for servlet mapping

I came across a baffling problem with my servlet mapping recently for my java web application. In this project we generally use extension-based url patterns for servlet mapping. For example:



<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

This means that every request that comes in that ends in ".html" will get passed into my servlet instead of being handled by Tomcat (who would serve it up like a static file). The problem came up when we could not use extension mapping because we had a few .html files that needed to be served up by Tomcat and the rest were dynamically generated by my servlet. But when I tried to use a path-based url pattern it didn't seem to work as I would expect.



Here is the situation: I know that all .html urls that have the word "library" in the path to need to be sent to my servlet and all other .html requests to be handled by Tomcat, so I tried the following:



<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/library/*</url-pattern>
</servlet-mapping>

My servlet is expecting requests to come in like "/library/index.html", and it worked with the extension url mapping, but with the path url mapping it didn't. It was like nothing was getting passed into my servlet. I did some searching and I didn't find anything in the Java Servlet Specification or on any blogs or forums that explained why path matching would be so different from extension mapping, until I came across this post where PhilipT in the comments explained how tomcat was stripping out the matched part: "So it seems that the Tomcat default servlet (6.0.16 at least) drops the servlet mapping and will try to find the file by using the remaining path." This made me wonder if that always happens with path matching.


I also learned from that post that you can map urls back to tomcat from your servlets web.xml file. In our case we haven't tweaked tomcats configuration so the default servlet is called "default". I let my servlet handle all .html files and I tell tomcat to handle things that come through with the words "static-content" in the path. Since the order of precedence is exact matches, path matches and then extension matches this works exactly as I want it to.



<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static-content/*</url-pattern>
</servlet-mapping>

The only catch with this solution is that you have to remember to put /static-content/ in front of the path to the static content that you want served up by tomcat. It is possibly confusing because there is no "static-content" directory on the server. To give an example, the html files that need to be served by Tomcat are for the fckeditor that we use in our web application. When you want to include a fckeditor on a page you have to tell the javascript where to find the fckeditor home directory. For our project it is a directory called fckeditor sitting in the war directory for our application. Previously we would just say that the home directory was /fckeditor/ and Tomcat would know to find it in <war directory>/fckeditor. Now that we are using path matching we have to tell the fckeditor javascript that its home directory is /static-content/fckeditor/, even though it is still physically on the server at <war directory>/fckeditor