5

I'm trying to find the best solution to manage browser cache to reload modified JavaScript/CSS resources in a Java/Maven project. The most widespread solution seems be Maven filtering to add a timestamp to the resource URL at build-time. For instance:

<script type="text/javascript" src="resource.js?v=${maven.build.timestamp}"></script>

But the most efficient way would be to add a checksum/hash of the file (aka fingerprint) instead of the build date so that the resource is not reloaded after each deployment but only when necessary. I'm looking desperately for the correct/generic implementation of this model using Java or a Maven plugin.

Any ideas?

Thanks.

benweet
  • 3,685
  • 1
  • 21
  • 26

2 Answers2

3

You do want to use fingerprinting vs a query param. The query param method doesn't always works and most proxies won't cache it. Changing the URL or actual file names works better.

So here's how I'm dealing with this in a Maven, Git, Tomcat, Dojo project. I use http://mojo.codehaus.org/buildnumber-maven-plugin/ to get my Git rev. And then use filtering when building my WAR to inject the value into my JSPs.

pom.xml

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>buildnumber-maven-plugin</artifactId>
        <version>1.1</version>
        <executions>
            <execution>
                <phase>validate</phase>
                <goals>
                    <goal>create</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <doCheck>false</doCheck>
            <doUpdate>false</doUpdate>
            <shortRevisionLength>8</shortRevisionLength>
            <revisionOnScmFailure></revisionOnScmFailure>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.3</version>
        <configuration>
            <warName>${project.name}-${project.version}-${buildNumber}</warName>
            <webResources>
                <resource>
                    <directory>src/main/webapp/WEB-INF/views/includes</directory>
                    <targetPath>WEB-INF/views/includes</targetPath>
                    <filtering>true</filtering>
                </resource>
            </webResources>                  
            ......
        </configuration>
    </plugin>

In my main JSP include I have

<script src="${pageContext.request.contextPath}/${buildNumber}/static/js/ckeditor/ckeditor.js"></script>
<script src="${pageContext.request.contextPath}/${buildNumber}/static/js/build/dojo/dojo.js"  data-dojo-config="parseOnLoad: true"></script>

To do the rewrite i'm using http://tuckey.org/urlrewrite/. I just have one simple rule.

my first filter entry web.xml

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    <init-param>
        <param-name>logLevel</param-name>
        <param-value>WARN</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

urlrewrite.xml

<rule match-type="regex">
    <from>^/[0-9A-Za-z_.\-]+/static/(.*)$</from>
    <to>/static/$1</to>
</rule>
denov
  • 11,180
  • 2
  • 27
  • 43
  • 1
    Thank you for the useful URL rewrite part. But the build number has pretty much the same behaviour as ${maven.build.timestamp}. Your assets will be re-downloaded after each new version even if they haven't changed. – benweet Jul 30 '13 at 12:24
  • maybe i miss understood your questions. are you wanting to force just the files that changed between deployments? – denov Aug 02 '13 at 20:36
  • i compile and minify all my js code into a couple files. so i really don't need to worry about what files have changes. user just have to download the js files on each update. – denov Aug 02 '13 at 20:43
1

I don't have use it yet, but the maven-fingerprint-plugin seems nice.

It requires just a bit of maven configuration, and then it will automatically rebuild all urls in your files to fingered versions.

No need to manually keep ${buildNumber}, ${hashVersion} or other fingerprint choice in your resources URLs.

From the repository:

<pluginRepositories>
    <pluginRepository>
        <id>fprint-repo</id>
        <url>https://raw.github.com/dernasherbrezon/maven-fingerprint-plugin/master/maven-fingerprint-plugin/mvn-repo</url>
    </pluginRepository>
</pluginRepositories>

then

<executions>
    <execution>
        <phase>package</phase>
        <goals>
            <goal>generate</goal>
        </goals>
    </execution>
</executions>
<configuration>
    <excludeResources>
        <excludeResource>://</excludeResource>
        <excludeResource>//</excludeResource>
    </excludeResources>
    <!-- ${basedir}/src/main/webapp by default -->
    <sourceDirectory>${basedir}/target/webcombined</sourceDirectory>
    <!-- ${project.build.directory}/fingered-web by default -->
    <outputDirectory>${basedir}/target/fingered</outputDirectory>
    <!-- Remove unnecessary spaces between tags. Make single line page.
    Takes into consideration <pre> tags -->
    <trimTagExtensions>
        <trimTagExtension>html</trimTagExtension>
    </trimTagExtensions>
    <extensionsToFilter>
        <extensionToFilter>html</extensionToFilter>
        <extensionToFilter>jsp</extensionToFilter>
        <extensionToFilter>tag</extensionToFilter>
        <extensionToFilter>css</extensionToFilter>
        <extensionToFilter>js</extensionToFilter>
    </extensionsToFilter>
    <!-- cdn host. Not required. For example using
    "//accountname.r.worldssl.net": /css/bootstrap.css =>
    //accountname.r.worldssl.net/css/<md5>bootstrap.css -->
    <cdn>${cdn}</cdn>
</configuration>

and that's it.

nyedidikeke
  • 6,899
  • 7
  • 44
  • 59
Xavier Portebois
  • 3,354
  • 6
  • 33
  • 53