Problem description

Another approach to the memoization topic mentioned previously came to me quite recently based on a problem with webservice call.

Imagine you have a webservice request/response exchange that’s taking a long time not only due to network latency but also due to the fact that your ws-logic performs a complicated ldap query.

So there are at least 3 reasons that might cause you a headache:

  1. slow internet connection
  2. slow ldap search
  3. big number of results that need to downloaded

In order to improve this situation results of method call might be cached inside ws-client’s code using ehacache. Cached entries will be identified based on method’s signature and passed arguments. The arguments should implement Serializable interface but this is already fulfilled since objects are passed through webservice calls.

Caching should be transparent to the caller – which should not know whether results he got were fetched from remote server or local cache. Additionally it should be configurable which methods calls should be cached or not and last but not least – since cached entries are identified by some key – it should be possible to define custom key-generator that would give the user freedom in defning key algorithm.

All the above requirements are summarized below:

  • webservice client api defined as interface
  • webservice client implementation that performs call to server
  • methods are chosen based on annotations
  • wraper defined as dynamic proxy so calls to client api are transparently may be cached and user is not aware if results come from cache or real call

Solution description

The solution will be spring-specific since Spring supports ehacache out-of-the-box.

Existing solutions

There exists already at least 3 solutions:

and somehow all of them combined fill the picture of what I’d expect from such a library:

Requirements
  1. Easy configuration with possibly as little xml as possible – smth like <context:component-scan> element with reasonable defaults
  2. Defining one annotation that enables method caching
  3. Including/Excluding methods from caching using annotation
Solution description

The approach descried in this post is realised by providing 3 main elements:

  1. Custom namespace handler implementing NamespaceHandlerSupport
  2. Custom bean definition parser extending AbstractSingleBeanDefinitionParser
  3. Custom BeanPostProcessor implmenting BeanFactoryAware interface
  4. Custom xsd

All points above respond to the requrements:

Points 1 and 2 let you declare usage of method cache with just one line of xml (after defining memoize namesapace) and creates EhCacheManagerFactoryBean and EhCacheFactoryBean behind-the-scenes freeing you from writing these explicitly inside you xml application context.

<memoize:use-method-cache
   config-location="classpath:ehcache.xml"
   cache-name="pl.bedkowski.code.memoize.sample.METHOD_CACHE" />

Point 3 finds classes annotated with Memoize annotation inside current context and wraps them inside Memoizer proxy, as well as fetches EhCacheFactoryBean instance created in the previous step.

@Component("wsClient")
@Memoize
public class WebServiceClientImpl implements WebServiceClient {

The Memoize annotation contains 2 properties:

  • keyGenerator – key generator class (must implement KeyGeneratorinterface)
  • type – one of Type.INCLUDE_ALL/Type.EXCLUDE_ALL

I think the last one needs some further explanation – it lets you define strategy for handling methods.

  • Default is Type.INCLUDE_ALL – which means that all methods that have non-void return type will be cached unless explicitly marked with Exclude annotation.
  • The reverse is Type.EXCLUDE_ALL which means that no method call will be cached unless marked with Includeannotation.

Sources, packages

Source available on github
Binaries available in repo

UPDATE: See updated version using ejb 3.1 compilant with any application server here.