This post explains (via a simple example) how you can use CDI Producers to make it a little easier to leverage cache control semantics in your RESTful services
The Cache-Control header was added in HTTP 1.1 as a much needed improvement over the Expires header available in HTTP 1.0. RESTful web services can make use of this header in order to scale their applications and make them more efficient e.g. if you can cache a response of a previous request, then you obviously need not make the same request to the server again if you are certain of the fact that your cached data is not stale!
How does JAX-RS help ?
JAX-RS has had support for the Cache-Control header since its initial (1.0) version. The CacheControl class represents the real world Cache-Control HTTP header and provides the ability to configure the header via simple setter methods. More on the CacheControl class in the JAX-RS 2.0 javadocs
So how to I use the CacheControl class?
Just return a Response object around which you can wrap an instance of the CacheControl class.
Although this is relatively convenient for a single method, repeatedly creating and returning CacheControl objects can get irritating for multiple methods
CDI Producers to the rescue!
CDI Producers can help inject instances of classes which are not technically beans (as per the strict definition) or for classes over which you do not have control as far as decorating them with scopes and qualifiers are concerned.
The idea is to
- Have a custom annotation (@CacheControlConfig) to define default values for Cache-Control header and allow for flexibility in case you want to override it
- Just use a CDI Producer to create an instance of the CacheControl class by using the InjectionPoint object (injected with pleasure by CDI !) depending upon the annotation parameters
- Just inject the CacheControl instance in your REST resource class and use it in your methods
- In this case, the scope of the produced CacheControl instance is @Dependent i.e. it will live and die with the class which has injected it. In this case, the JAX-RS resource itself is RequestScoped (by default) since the JAX-RS container creates a new instance for each client request, hence a new instance of the injected CacheControl instance will be created along with each HTTP request
- You can also introduce CDI qualifiers to further narrow the scopes and account for corner cases
- You might think that the same can be achieved using a JAX-RS filter. That is correct. But you would need to set the Cache-Control header manually (within a mutable MultivaluedMap) and the logic will not be flexible enough to account for different Cache-Control configurations for different scenarios
Results of the experiment
Use NetBeans IDE to play with this example (recommended)
- Deploy the WAR and browse to http://localhost:8080/JAX-RS-Caching-CDI/testcache
- A random string which would be cached for 20 seconds (as per configuration via the @CacheControl annotation)
- A GET Request to the same URL will not result in an invocation of your server side REST service. The browser will return the cached value.
Although the code is simple, if you are feeling lazy, you can grab the (maven) project from here and play around