Caching WSDL Documents Generated by Spring Web Services

Preface

The Jasig Resource Server can easily be used to cache generated content from a wide range of applications. This document will describe how one can cache the WSDL documents that are automatically generated by Spring Web Services.

This document is really geared towards applications that are already using Spring and Spring Web Services, and possibly are already using Ehcache via Spring's EhCacheManagerFactoryBean and/or ehcache-spring-annotations.

Background

Spring Web Services "is a product of the Spring community focused on creating document-driven Web services. Spring Web Services aims to facilitate contract-first SOAP service development, allowing for the creation of flexible web services using one of the many ways to manipulate XML payloads."[1]

One of Spring Web Services powerful features is the ability to automatically generate the WSDL document for your web service application.[2] It can also transform the "location" attribute at runtime.

Both of these generation processes use XSLT to construct the final result document each time it is requested, which can be CPU expensive. If the transformLocations directive is set to true, the document is transformed twice.

Adding the caching filter around this saves more CPU resources for your application's other activities.

Note that Spring Web Services cautions against using dynamic WSDL generation in production applications to avoid accidentially publishing changes[3]. This is a good advice, but Spring's WsdlDefinitionHandlerAdapter will still use XSLT to transform your WSDL document no matter your choice of static or dynamic construction. These instructions on how to cache the generated WSDL still apply.

Caching Generated WSDL Documents

Add the Jasig Resource Server to your project's Maven pom.xml dependencies section:

<dependency>
 <groupId>org.jasig.resourceserver</groupId>
 <artifactId>resource-server-utils</artifactId>
 <version>1.0.24</version>
</dependency>

The resource server's dependencies include Ehcache and the ehcache-web sub-project.

Add the following to your Spring applicationContext:

<!-- Your application may already define this bean if you are caching other parts of your application, it's included here to show the relationship to filter -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
 <property name="cacheManagerName" value="MyApplicationCacheManager"/>
</bean>
 
<!-- the ID attribute here must match filter-name in the web.xml -->
<bean id="WsdlCachingFilter" class="org.jasig.resourceserver.utils.cache.ConfigurablePageCachingFilter">
 <constructor-arg ref="cacheManager"/>
 <constructor-arg value="WsdlCache"/>
</bean>

Add a filter element to your web application's web.xml:

<filter>
 <filter-name>WsdlCachingFilter</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 <init-param>
 <!-- targetFilterLifecycle=true is REQUIRED when initializing the ConfigurablePageCachingFilter in this fashion -->
 <param-name>targetFilterLifecycle</param-name>
 <param-value>true</param-value>
 </init-param>
</filter>
<filter-mapping>
 <filter-name>WsdlCachingFilter</filter-name>
 <url-pattern>*.wsdl</url-pattern>
</filter-mapping>

Lastly define your cache in ehcache.xml; the following example will cache the WSDL for the lifetime of the application:

<!-- cache name attribute must match the 2nd argument to the 'WsdlCachingFilter' in the Spring applicationContext
<cache name="WsdlCache" eternal="true"
 maxElementsInMemory="100" overflowToDisk="false" diskPersistent="false"
 memoryStoreEvictionPolicy="LRU" statistics="true"/>

Alternative Configuration

The Ehcache Web sub-project that is a dependency of the Jasig Resource Server includes a 'SimplePageCachingFilter' that could be used directly. The problem with that approach is that the SimplePageCachingFilter gets a reference to a CacheManager using the static CacheManager.html#getInstance() function. If you have a CacheManager defined in your Spring application, using SimplePageCachingFilter directly means your Spring configured instance won't be used.

References

[1] http://static.springsource.org/spring-ws/sites/2.0/

[2] http://static.springsource.org/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure

[3] See the "Caution" section under http://static.springsource.org/spring-ws/site/reference/html/server.html#server-automatic-wsdl-exposure