views:

362

answers:

3

As per the Q&A here, I'd like to implement a similar auto-versioning system for a web app running in JBoss 5. Is there anything already out there to do this sort of thing, or will I need to write something myself? To be clear: I am not using PHP.

Not knowing much about PHP, I'm not sure what the Tomcat/JBoss analogs of PHP's .htaccess, etc. are. If I do have to write my own auto-versioning, where would I start? The principle is clear to me - rewriting the URL using the file's timestamp, but I don't know much about URL rewriting with JBoss/Tomcat.


Update:

Combining the approaches recommended by Pascal and novice, here's what I ended up with:

1. Custom <my:script/> and <my:style/> tags, so I wouldn't have to see <c:url/> tags everywhere.

<%@ tag body-content="empty" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ attribute name="src" required="true" rtexprvalue="true" %>
<script src="<c:url value="${src}" />"></script>

2. Following fairly closely to novice's steps, but mapping UrlRewriteFilter to /* in web.xml:

<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3. Injecting a CACHE_BUST variable to every new session (more or less...), an application deploy timestamp:

// On application deploy:
long CACHE_BUST = System.currentTimeMillis() / 1000;

// later...
session.setAttribute("cacheBust", CACHE_BUST);

4. ...so that I can use these rules in urlrewrite.xml:

<outbound-rule>
    <from>^/static/(css|js|images)/(.*)$</from>
    <to>%{context-path}/static/%{session-attribute:cacheBust}/$1/$2</to>
</outbound-rule>

<rule>
    <from>^/static/\d{10}/(css|js|images)/(.*)$</from>
    <to>/static/$1/$2</to>
</rule>

Many thanks to Pascal and novice for their help.

+2  A: 

If you don't want to front your application with Apache HTTPD, then you could use a custom servlet filter or reuse the existing Url Rewrite Filter. This filter is based on Apache's mod_rewrite and offers similar capabilities. In other words, it would allow to implement the same solution than the PHP one of the other answer.


I've seen the URL rewrite filter before. Could you elaborate on how I'd use that? I'm really not clear on how I'd apply a filter to this problem, since I wouldn't exactly be calling a JSP/JSTL function wrapped around each included JS/CSS file, and I have no idea about how to get the date modified from a file that's in a WAR.

Well, the idea was to mimic exactly the "PHP solution" of the answer you linked to (let's call this option 1):

  1. Setup the Url Rewrite Filter to rewrite any request to say /css/my.123456.css into /css/my.css
  2. Implement a Servlet that would get a File object for a given resource inside the WAR and insert File#lastModified() in the returned path to that resource.
  3. Invoke that Servlet from the JSP for the CSS.

Another approach (option 2) would be to append an unique query string to the URL of the static content, e.g. the server startup time:

  1. Put the server startup time in the application scope from a ServletContextListener (say under the key "key").
  2. In you JSP

    <link rel="stylesheet" type="text/css" href="/css/my.css?${key.startupTime}">
    

Pro: no url rewrite stuff anymore. Con: Less optimal (content will be request on restart) but acceptable.


While searching the web for some code that could help to implement the step #2 of option 1, I found Spring's o.s.w.s.ResourceServlet that is doing something similar, you could look at its source code. But, while reading its javadoc more carefully, I realized that this servlet is actually exactly what you're looking for. Map it like this:

<servlet>
  <servlet-name>Resource Servlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.ResourceServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>Resource Servlet</servlet-name>
  <url-pattern>/static/*</url-pattern>
</servlet-mapping>

And set its applyLastModified property to true. My understanding of the javadoc is that it should do the trick. This is option 3 and this is IMO the best option if adding a dependency on this servlet in not an issue.

Pascal Thivent
I've seen the URL rewrite filter before. Could you elaborate on how I'd use that? I'm really not clear on how I'd apply a filter to this problem, since I wouldn't exactly be calling a JSP/JSTL function wrapped around each included JS/CSS file, and I have no idea about how to get the date modified from a file that's in a WAR.
Matt Ball
A: 

We do the following in our web app:

  1. Build process retrieves Subversion repository number and stores it in a property within the web application.
  2. Build process also creates a directory structure for static assets within the WAR that incorporates this revision number: /assets/1234/styles/...
  3. Filter/interceptor puts assets path (including revision number) into all requests as attribute
  4. Jsp templates use this assets path attribute to construct URLS for assets
Eric Rath
That's not that helpful. Like I said, I get the principle; I'm looking for implementation specifics.
Matt Ball
I'm not aware of anything "built in" that specifically handles this issue. What I described above is exactly our implementation, and works well enough for us.
Eric Rath
+1  A: 

Following solution is better suited in production environment as you would be incrementing the version number for each release.

Approach:

  • append the product release number(version number) to the js/css/images/static content urls in jsp files
  • browser would cache (js/css/static)files with the url containing the version number
  • when a new version is released, the jsp files will have the urls of js/css/static files with new version number, so the browser will make a call as it can't find the content for the new url

Steps:

  • include urlrewritefilter.jar in the class path (get it from http://www.tuckey.org/urlrewrite/)
  • update web.xml with the url pattern, for e.g,

    <filter>
        <filter-name>urlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>
    
    
    <filter-mapping>
      <filter-name>urlRewriteFilter</filter-name>
      <url-pattern>/v/*</url-pattern>
    </filter-mapping>
    
  • update 'abc.js' in the jsp file, for e.g.,

    <html>
        <head>
            <script type="text/javascript" src="<c:url value="/v/js/abc.js"/>"></script>
        </head>
    </html>
    
  • write urlrewritefilter.xml, for e.g.,

    <outbound-rule>
        <from>^/v/(css|js|static)/(.*)$</from>
        <to>%{context-path}/v/updateVersionNumberHere/$1/$2</to>
    </outbound-rule>
    
    
    <rule>
        <from>^/v/updateVersionNumberHere/(css|js|static|images)/(.*)$</from>
        <to>/$1/$2</to>
    </rule>
    

    Explanation:

    when jsp is served to client

    • url mentioned in the jsp: /v/js/abc.js
    • after applying outbound rule: /contextPath/v/3.4.5/js/abc.js

    when browser makes a call for js/css/static files

    • incoming url: /contextPath/v/3.4.5/js/abc.js
    • after applying rule: /js/abc.js

Points:

  • browser would catch the js files with url /contextPath/v/3.4.5/js/abc.js, next you deploy a new version of jsp files they might have the url /contextPath/v/4.5.6/js/abc.js, so the browser would make a call for js file instead of using the cached js file.
  • version updating could be automated if you use maven or similar build tool
novice
This looks very promising. Will test out ASAP. Might combine with @Pascal's server startup time suggestion.
Matt Ball
That spelled it out beautifully. Thank you!
Matt Ball