CAS 5.0.6, VersionResourceResolver and Cache-Busting

428 views
Skip to first unread message

John Sampson

unread,
Jan 11, 2018, 12:45:50 PM1/11/18
to CAS Community
Hello, I'm currently upgrading a highly customized 4.X CAS version to 5.0.6 and running into a problem with cached assets on our F5 Appliance. 

The problem is that many of the customized assets have the same name (ex. cas.js) but different content, and while we can clear the F5 Ram Cache, this does not allow us the flexibility to switch cleanly back and forth between the two versioned deployments and we would rather add a VersionResourceResolver to the newer version (5.0.X) of the application so that all assets have hash codes in the file names so that the F5 appliance will cache the proper version, and the two applications will not be getting one another's assets. 

 If someone has already done this and could provide some pointers I would appreciate it. I think I'm half way there, but am not sure I'm not mixing solutions. We have a desire to get away from our highly customized versions and back inline with the direction of the underlying CAS software (and this is out first step).

I've read several articles and gotten as far as encoding the urls in the templates, but when the login page is accessed, the server returns a 404 for the javascript assets, so here is what I have in the 5.0.6 application:

Background/Environment:

 
  • Software: CAS 5.0.6 built with an overlay
  • Application.properties in src/main/resources
  • JavaScript resources in: src/main/resources/static/js
  • Thymeleaf templates in in: src/main/resources/static/templates
  • Environment:
    •  Local: build and run locally via a fat jar.
    •  Everywhere else: fat jar built on Jenkins, Docker Image deployed to K8S Cluster with an F5

Resources I've reviewed:

  • CAS User Interface Customization (No real info)
  • Spring Boot 1.4.2 Reference (Static Content, which suggests that the ResourceUrlEncodingFilter should be AutoConfigured and handled internally by Thymeleaf but it doesn't seem to be, which is why I added the bean and additional syntax to the templates below).

Application.properties
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
StaticResourceConfig.java
@Configuration
public class StaticResourceConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        VersionResourceResolver versionResourceResolver = new VersionResourceResolver()
                .addVersionStrategy(new ContentVersionStrategy(), "/js/*.js");

        registry.addResourceHandler("/js/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(60 * 60 * 24 * 365) /* one year */
                .resourceChain(true)
                .addResolver(versionResourceResolver);
    }

    /**
     * Enables usage of versioned assets in thyme leaf templates.
     * @return
     */
    @Bean
    public ResourceUrlEncodingFilter resourceUrlEncodingFilter(){
        return new ResourceUrlEncodingFilter();
    }
} 
LoginForm.html 

 ... 
<script type="text/javascript" th:src="${@mvcResourceUrlProvider.getForLookupPath('/js/login.js')}" /> 
...
 
The the script tag above  is rendered properly in the page by the ResourceUrlEncodingFilter:

<script type="text/javascript" src="/js/login-5082a374b0db842cb10cbe96ee10ecbc.js" />

However, the server responds with a 404 when the file is requested. 

Conculsion:

I feel like I'm missing some component that maps login-5082a374b0db842cb10cbe96ee10ecbc.js back to login.js when the request is received but don't know where to go and debug to look for it. 

In my Google searches, I've seen a few bugs listed with regards to the ordering of Resolvers and the Ant Path Matching Patterns in Spring/SpringBoot, but am just getting around to figuring out which classes log to debug and can report back when I find anything there, but wanted to get this email in the event anyone has some experience or advice with this.

Thanks,

-John

John Sampson

unread,
Jan 12, 2018, 9:52:37 AM1/12/18
to CAS Community
Some further information...

We now think that perhaps this issue is due to the the Thymeleaf  configuration and are investigating that.

Reasoning: 

Using this blog as a base and modify it's source example to use Spring Boot 1.4.2 with spring-boot-starter-thymeleaf and setting the  server.contextPath to  /cas  in application.properties, we see that an anchor like:

<a th:href="${@mvcResourceUrlProvider.getForLookupPath('/javascript/test.js')}">Test</a>

Will render as:

<a href="/cas/javascript/test-44636fcec8502b1e2cb022922a819a53.js">Test</a>>

Where as the same tag in CAS loginForm.html will render without the root context:

          <a href="/javascript/test-44636fcec8502b1e2cb022922a819a53.js">Test</a>

So I'm thinking there must be a different between the spring-boot-starter-thymeleaf configuration and CAS's Thymeleaf configuration. Perhaps in How the SpringEL set up of dialect?

Can anyone give me a pointer at to where to look for that in CAS code?

John Sampson

unread,
Jan 13, 2018, 12:56:11 PM1/13/18
to CAS Community
Solved it.

Turns out this was simply a 'PEBCAK' problem; most likely due to a missing annotation and misunderstanding of the SpringStandard dialect.

I'll post the solution here the event that anyone needs it when upgrading CAS versions and switching between new and old versions with a network caching layer and modifications to similar named assets.
  1. Add ResourceHandlers for static assets: 

    @EnableWebMvc
  1. @Configuration
    public class StaticResourceConfig extends WebMvcConfigurerAdapter {

  1.     /**
    * Adds a handler to serve static resources such as images, js, and, css files
    * from specific locations under web application root, the classpath, and others.
    *
    * @param registry
    */
  1.     @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
  1.         super.addResourceHandlers(registry);

    // This Registry handles CAS resources we may need to modify between versions.
    // The ContentVersionStrategy ensures that if the file content changes, the file name changes
    // and the both the both the browser cache and the network cache will ask for the proper version.
    registry.addResourceHandler("**/*.js", "/**/*.css", "/**/*.png", "/**/*.jpg", "**/font-awesome-4.3.0/*", "**/fonts/*")

  1. .addResourceLocations("classpath:/static/")
    .setCachePeriod(60 * 60 * 24 * 365) /* one year */
    .resourceChain(true)
  1.                 .addResolver(new VersionResourceResolver()
    .addVersionStrategy(new ContentVersionStrategy(), "/**"));
    }
    }

  2. Update References to assets that need to be versioned

    From : 
    <script type="text/javascript" th:src="@{/js/myScript.js}"></script>

    To:
    <script type="text/javascript" th:src="@{${@mvcResourceUrlProvider.getForLookupPath('/js/myScript.js')}}"></script>

    Note:  Assets can still be referenced by their non-hashed names so you only have to update references to files that will change between versions
    Note:  
    spring.resources did not require modification from overlay defaults


On Thursday, January 11, 2018 at 10:45:50 AM UTC-7, John Sampson wrote:

Sam Hough

unread,
Aug 30, 2019, 6:43:58 AM8/30/19
to CAS Community, jtsamp...@gmail.com
Alternative solution (definitely not saying better):

Have own version of cas-theme-default.properties
cas.standard.css.file=/css/cas.css?v=${project.version}
cas
.admin.css.file=/css/admin.css?v=${project.version}
cas
.javascript.file=/js/cas.js?v=${project.version}

Get Maven to "filter" this file (stupid name), seems horribly verbose but best I've found is:
        <resources>
           
<resource>
               
<directory>src/main/resources</directory>
               
<filtering>false</filtering>
               
<excludes>
                   
<exclude>**/cas-theme-default.properties</exclude>
               
</excludes>
           
</resource>
           
<resource>
               
<directory>src/main/resources</directory>
               
<filtering>true</filtering>
               
<includes>
                   
<include>**/cas-theme-default.properties</include>
               
</includes>
           
</resource>
       
</resources>
   
</build>
Easier in other build systems?

Bit shameful when this problem bit me.

Cheers

Sam
Reply all
Reply to author
Forward
0 new messages