Search code examples
javaservletsurl-mapping

Java servlet web project custom URL resource mapping for the purpose of release cache prevention


The answer to this question may already be online, but I cannot figure out how to ask it without getting irrelevant results. In a Java web project where I use servlets and have a web.xml deployment descriptor, aside from servlets, is there a way to create custom mappings for any URL resource? For example, html files, Javascript files, images, style sheets, etc.?

The reason I ask has to do with browser caching. Between releases of a web project, if a resource gets cached, and then an update rolls out, the browser will typically try to load the cached version first. In places where DOM elements and Javascript functions have changed or been updated, this can lead to the updated page just breaking due to unsynchronized resources.

Now I have heard of a lot of solutions to this problem:

  • Add a URL parameter query string to all the resources. Therefore, index.html becomes index.html?v=1.2 in one release, and then index.html?v=1.3 in the release after, preventing cache overlap. My worry with this is that not all browsers implement a caching policy that respects this. Issue: Where one browser may cache the file as index.html?v=1.3, another browser may just cache index.html and add the URL parameters to it after loading it from the cache, and another browser may just not cache files with URL parameters at all.
  • touch all files on the server between releases. This way, when the HTTP request for a resource is sent, and the response header shows that the retrieved file has a newer timestamp than that of the cache, it will reload. Issue: Again, I am not certain that all browsers implement any such caching policy.
  • Implement logic in my Javascript files where all versions must match, and make the very first Javascript file that loads be a dynamic (uncachable) file that provides the master version key. Any javascript files that are out of sync (i.e. the version does not match the master version because it was cached) will be force reloaded. Issue: this actually sounds like a good approach, but it is frightening to rely on added logic in a Javascript file when I know that caching has been an issue in the past; as in, will my new "version check" logic even be loaded?
  • Merge the version in with the file name. Therefore, index.html becomes index.1.2.html. This would be the end-all be-all solution, since caching is done at the file name level, and when we move to release version 1.3, there is no possible way the browser already has index.1.3.html cached already. Issue: Source control management on the development side becomes a nightmare...

...Unless, there was a way to map index.1.3.html to a server-side resource simply named index.html. This gets back to my original question, can this be done in a web project? Is this even recommended? I know in web.xml, we can map URL patterns to servlets, but can we have URL patterns mapped to other resources? It seems it would be so easy to just maintain a single descriptor file between releases, so that on the client end, it appears that all files are new, so there will definitely be a loading hit the first time the new release is loaded, but caching out of sync resources would be eliminated this way.


Solution

  • A viably simple solution to this issue in a Java web application would actually not require mapping, nor would I have to alter the web.xml deployment descriptor at all. Instead, the method of building the web project would simply have to change ~ which typically means altering the build script. A slightly different approach to merging the version with the file name would be to merge the version with the URL path. For example, index.html would be accessed via HTTP here for release 1.2:

    /doc-1.2/index.html
    

    And then for release 1.3, it would be accessed here:

    /doc-1.3/index.html
    
    • In your actual web project (in eclipse or where ever you prefer this be), the location of index.html, as well as all other HTTP resources would remain untouched. This solves the source control issue.
    • In your index.html file, all web-application-specific resources would be required to be relative to the path of index.html. This relieves the need for having an extra component, such as a servlet, embed the version number into each referenced resource when the file is loaded from the server.
    • The entry point of the web application, say / (as in http://hostname.domain.com/AppName/) would be a servlet that redirects to the actual entry point based on the current release. For example, in release 1.2, the entry point / (or index.jsp) would do a simple redirect to doc-1.2/index.html, and then this would change to doc-1.3/index.html in release 1.3.
    • However the web application is built, whether through Ant or BuildForge or whatever tool, the build script would have to be modified to move all resources in the web project's WebContent directory to a destination point of doc-<release #> in the build web application.

    Therefore, the only two things that would have to change between releases are the entry point redirection and the build script. As mentioned in the original question, when a new release is deployed, the entry point will now redirect the client web browser to doc-1.3/index.html, and all resources will now be relative to the path doc-1.3, and there is no possible way that the browser could already have anything cached with a prefix of doc-1.3.

    The one issue that this method does not address is what happens if a client bookmarks a version dependent resource in their browser, say http://hostname.domain.com/AppName/doc-1.2/index.html, when the newest release version is 1.3. They would ultimately get a 404 because on the server, no such doc-1.2 directory exists (as a result of the build script only creating the directory necessary for the current release). The simplest solution around this problem that I can think of would be to make one additional servlet, and map all previous release versions to it. The servlet would always either redirect the user to the actual entry point, or redirect the user to the same resource but in the current release. So by the time the web application is to release 1.5, the "previous version redirect servlet" would be mapped to all of /doc-1.0/**, /doc-1.1/**, /doc-1.2/**, /doc-1.3/**, and /doc-1.4/** (which includes all sub-paths of those directories). This solution would simply require that all previous versions be accounted for and maintained in this servlet mapping.