Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fingerprinting Assets in a Maven/JSP/RequireJS Webapp?

I have a simple Maven webapp that uses a single JSP and RequireJS to serve up a single-page Javascript-heavy app. I've been looking around for something that I can use to fingerprint assets during the build process (.js, .css, etc) but have not been finding anything that solves this problem.

I'd like asset file names to change whenever the content changes so I can tell the browser to cache them for a really long time, yet still download the latest whenever they change. I'll also need any references to those assets to be updated whenever they change. Whatever I use will have to work with RequireJS as well.

Any suggestions?

like image 282
erturne Avatar asked Oct 21 '22 18:10

erturne


2 Answers

I solved this problem recently by using RequireJS's urlArgs config option. I doubt actually renaming files to contain the timestamp would be feasible, it would complicate build and RequireJS configuration a lot and would most likely require hacks for either development or production. So, in logical order:

  1. in pom.xml:

    <properties>
        (...)
        <build.version>${maven.build.timestamp}</build.version>
    </properties>
    
  2. in the main JSP file:

    <script type="text/javascript">
        var require = {
            (...)
            urlArgs: 'v=${build.version}',
        };
    </script>
    
    <link rel="stylesheet" type="text/css" href="style.css?v=${build.version}"></link>    
    <script data-main="app" src="libs/require.js?v=${build.version}"></script>
    

    It's important to define the require object before the require.js import to make urlArgs work

  3. pom.xml, again:

    <resources>
      (...)
      <resource>
        <targetPath>${project.build.directory}/filteredWebapp</targetPath>
        <directory>src/main/webapp</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
    
  4. r.js's buildconfig (btw. I'm using requirejs-maven-plugin to bridge Maven and r.js):

    ({
        appDir: '${project.build.directory}/filteredWebapp',
        dir: '${project.build.directory}/${project.build.finalName}',
        (...)
    })
    

    I had to store result of filtering src/main/webapp in a new folder (ie. filteredWebapp) to make sure r.js' input will already contain the build timestamp and not the placeholders. My initial version of r.js buildconfig was reading the source files directly from src/main/webapp; that worked OK but was bypassing maven filtering (ie. output compiled by r.js still contained the ${build.timestamp} placeholders)

Note: it's probably tricky to configure the development version to use this mechanism (if possible at all, I haven't tried). For me the presence of "v=${build.timestamp}" parameter and lack of cache busting was not a problem in practice, though.

There were many other obstacles and pitfalls along the way but I'm assuming you managed to solve those since you're only specifically asking for fingerprinting. Hope that helps!

like image 197
kryger Avatar answered Oct 24 '22 10:10

kryger


I would actually strongly discourage using query parameters as "cache killer" as this may result in what's called a cache poisoning. For example: lets say you have a cdn or a front end caching server and multiple app servers. You upload a new version of a file and change the query param. However the new file still wasnt. Delivered to all app servers . The cache server then goes to one of the servers(since it sees that query param has changed) that still contains an old version, gets it and caches it. So now the cache server has and OLD file with A new cache killer thinking its up to date and no longer tries to get it from the server.

like image 43
Boris Litvinsky Avatar answered Oct 24 '22 11:10

Boris Litvinsky