javaspringspring-bootspring-mvcspring-webflow

Versioning static assets does not work with Spring Web Flow


Cache expiration and versioning of resource file work correctly on all pages. But flows seem to ignore Spring MVC configuration.

A working example:

resource files have versioning

With Spring Web Flow:

resource files are missing versioning

In WebMvcConfig class:

@Configuration
@EnableCaching
@ConfigurationProperties("message")
public class WebMvcConfig implements WebMvcConfigurer, ServletContextAware {

    ...

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/")
                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                .resourceChain(false)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                .addTransformer(new CssLinkResourceTransformer());
    }

    @Bean
    public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
        return new ResourceUrlEncodingFilter();
    }

    ...
}

WebConfigClass:

@Configuration
@EnableWebMvc
public class WebFlowConfig extends AbstractFlowConfiguration {
    @Autowired
    private ViewResolver viewResolver;
    @Autowired
    private RequestDataInterceptor requestDataInterceptor;
    @Autowired
    private LocalValidatorFactoryBean validator;

    // WEB FLOW
    @Bean
    public FlowExecutor flowExecutor() {
        return getFlowExecutorBuilder(flowRegistry()).addFlowExecutionListener(new SecurityFlowExecutionListener(), "*").build();
    }

    @Bean
    public FlowDefinitionRegistry flowRegistry() {
        return getFlowDefinitionRegistryBuilder(flowBuilderServices()).setBasePath("/WEB-INF/flows/").addFlowLocationPattern("/**/*-flow.xml").build();
    }

    @Bean
    public FlowBuilderServices flowBuilderServices() {
        return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator()).setValidator(validator).setDevelopmentMode(true).setConversionService(getDefaultConversionService()).build();
    }

    private DefaultConversionService getDefaultConversionService() {
        final DefaultConversionService service = new DefaultConversionService();
        final FormattingConversionService delegateConversionService = (FormattingConversionService) service.getDelegateConversionService();
        delegateConversionService.removeConvertible(String.class, Number.class);
        delegateConversionService.addConverterFactory(new StringToNumberConverterFactory());
        delegateConversionService.addConverter(new TrimStringConverter());
        return service;
    }

    // MVC
    @Bean
    public FlowHandlerMapping flowHandlerMapping() {
        final FlowHandlerMapping mapping = new FlowHandlerMapping();
        mapping.setOrder(0);
        mapping.setFlowRegistry(this.flowRegistry());
        mapping.setInterceptors(requestDataInterceptor);
        return mapping;
    }

    @Bean
    public FlowHandlerAdapter flowHandlerAdapter() {
        final FlowHandlerAdapter adapter = new FlowHandlerAdapter();
        adapter.setFlowExecutor(this.flowExecutor());
        adapter.setSaveOutputToFlashScopeOnRedirect(true);
        return adapter;
    }

    @Bean
    public MvcViewFactoryCreator mvcViewFactoryCreator() {
        final MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
        factoryCreator.setViewResolvers(Lists.newArrayList(this.viewResolver));
        factoryCreator.setUseSpringBeanBinding(true);
        return factoryCreator;
    }
}

In security config:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ... 

    @Override
    public void configure(final WebSecurity web) {
        web.ignoring().antMatchers("/resources/**");
    }

    ...
}

In jsp files (including flow):

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
...
<!-- jQuery -->
<script type="text/javascript" src="<c:url value="/resources/scripts/libs/jquery-3.3.1.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-validation/jquery.validate.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-numbers/jquery.number.min.js"/>"></script>
...

Any ideas on how to apply versioning of static assets on flows?


Solution

  • my way to deal with it:

    <spring:eval expression="@applicationProperties.resourcesVersion" var="resourcesVersion"/>
    <head>
        <style type="text/css" media='screen,print'>
            @import url("<c:url value="/resources/css-framework/css/tools.css?v=${resourcesVersion}" />");
        </style>
        <script type="text/javascript" src="<c:url value="/resources/spring/Spring.js?v=${resourcesVersion}" />"></script>
    </head>
    

    ApplicationProperties:

    @Named
    public class ApplicationProperties {
    
        ....
    
        private Long resourcesVersion = System.currentTimeMillis();
    
        public Long getResourcesVersion() {
            return resourcesVersion;
        }
    }