Visitors are also reading...
← PreviousNext →

Serving Static Files From JAR

01 Apr 2013

If you have a lot of static files (images, JS, css) in your WAR,
Or maybe you have static files that are shared between different projects
you might want to pack those resources into a JAR and serve them from there.

Make Sure Your Class Files Are Packaged

First of all, if you are looking to reduce the number of files in your WAR
You should check if your ".class" files are packaged into a JAR.
You can easily achieve this by configuring the Maven WAR Plugin

<plugin>  
 <groupid>org.apache.maven.plugins</groupid>  
 <artifactid>maven-war-plugin</artifactid>  
 <version>2.3</version>  
 <configuration><archiveclasses>true</archiveclasses></configuration>   
</plugin>

Package Your Static Resources

Lets assume that you have the following directories

src  
 |  
 +---main  
       |  
       +----java  
       |  
       +----resources  
       |  
       +----webapp  
              |  
              +-----public  
              |        |  
              |        +-----images  
              |        |  
              |        +-----javascripts  
              |        |  
              |        +-----pages   
              |        |  
              |        +-----stylesheets         
              |  
              +-----WEB-INF  
              |  
              +-----META-INF  
              .  
              .  
              .

In this structure, your static resources are under folder "webapp/public".
In order to package them, simply move the "public" folder under "resources".
Next, you will have to define a servlet to server them.

Configuring Servlets

Assuming you already have Spring MVC (if not, it's not a problem to set it up quickly)
All you need to do, is add the following to the web-context.xml

<mvc:resources mapping="/public/**"  location="classpath:/public/" />

However, since I use annotations, I ran into some problems.
The first exception was

Does your handler implement a supported interface like Controller?

I quickly resolved this by using

<mvc:annotation-driven/>

instead of

<context:annotation-config/>

It turns out that using "resources" tag, shuts down an important annotation configuration in Spring.

Once I added the "annotation-driven" tag, I got

Error creating bean with name org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

Unfortunately, I was unable to resolve this nicely.
I was forced to add hibernate-validation JAR to my project.

<dependency>  
 <groupid>org.hibernate</groupid>  
 <artifactid>hibernate-validator</artifactid>  
 <version>4.0.0.GA</version>  
</dependency>

Last But Not Least - Configure Web.xml

This is my web.xml - yours might be a bit different, depends on how you use Spring MVC.

<servlet>  
    <servlet-name>Spring</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   
    <init-param><param-name>contextConfigLocation</param-name>  
        <param-value>/WEB-INF/web-context.xml</param-value></init-param>   
    <load-on-startup>1</load-on-startup>  
</servlet>     
<servlet-mapping>  
    <servlet-name>Spring</servlet-name>  
    <url-pattern>*.do</url-pattern>  
</servlet-mapping>  
<servlet-mapping>  
        <servlet-name>Spring</servlet-name>  
        <url-pattern>public/*</url-pattern>  
</servlet-mapping>

Please note I have 2 servlet-mappings. It seems to work just fine.

Troubleshooting

When I configured spring MVC, I kept getting "404".
I felt the need to debug the servlet. But which servlet?
To figure this out, you need to read the spring mvc XSD!

<xsd:annotation>
  <xsd:documentation
   source="java:org.springframework.web.servlet.resource.ResourceHttpRequestHandler"><![CDATA[
Configures a handler for serving static resources such as images, js, and, css files with cache headers optimized for efficient
loading in a web browser. Allows resources to be served out of any path that is reachable via Spring's Resource handling.
  ]]></xsd:documentation>
 </xsd:annotation>

So I added break points on the class :

org.springframework.web.servlet.resource.ResourceHttpRequestHandler

Which worked just fine.
The code is written well.
It was very easy for me to understand the flow and my errors.

A Word About Resource Mapping

I want to talk about the resources mapping in web-context.xml a minute.

<mvc:resources mapping="/public/**"  location="classpath:/public/" />

First of all, if I were to serve the content from outside the jar the mapping would simply be

<mvc:resources mapping="/public/**"  location="/public/" />

So the different between packaging and not packaging is whether it is in the classpath or not.
Another important thing to note is that I specify "public" twice. Once in "mapping" and again in "location".
I do this because the "public" prefix is stripped from the URI due to "mapping".
By adding it to location I am actually returning the prefix to the URI
So if I have a resource under

my.jar!/public/images/logo.png

which I ask for by writing

<img src="/public/imags/logo.png"/>

after "mapping" it, spring will have "images/logo.png".
If I were to write the location as "classpath:/guy/" - spring would look for it under

my.jar!/guy/images/logo.png

By specifying classpath:/public/ I am actually fixing the prefix back to public.

← PreviousNext →