Sunday, June 04, 2006

Load a Spring Resource relative to your context file

In MemeStorm's Loading resources from files or the classpath, I left this comment:
I was surprised how hard it was to get XmlWebApplicationContext to use a resource root *other than* the root of the webapp. For security reasons you usually want your configuration files under /WEB-INF/, not the actual webapp root. But if you place them there, your app context XML file has to reference “/WEB-INF/db/init.sql” — which means that configuration file is no longer useful outside of a web container context.

In solving this problem, I initially tried to subclass XmlWebApplicationContext to remember the directory it was loaded from and use that as its base when loading other resources. However, the loading of the actual context file was buried deep within the bowels of its code, making it difficult to retain knowledge of the directory it was loaded from without slicing through a deep chain of method signatures.

I settled on a less elegant but quicker solution. I added a new bean property "contextRoot" that is prepended to every path passed into the getResourceByPath method.

public class ChrootXmlWebApplicationContext
 extends XmlWebApplicationContext {

 protected Resource getResourceByPath(String path) {
   return super.getResourceByPath( getContextRoot()
     + path.startsWith("/") ? path : "/" + path);

The contextRoot property is pulled from a servlet context init parameter the first time it is requested; trailing slashes are trimmed. When prepending it to a resource path, a slash is reapplied between the contextRoot and the requested path, if necessary, as shown above.

By placing the context file in WEB-INF/ and setting contextRoot to "WEB-INF/" as well, you can mimic the behavior that would occur if the context was loading files relative to itself.