Well, OK, maybe a lot more, but these technologies just work so sweet together that we decided to jump in and give it a go at the Devoxx UK 2015 Hackergarten. Heather VanCura, a leader of the Java Community Process (JCP), clubbed several developers together for some fun key bashing and this is the result. Enjoy!
This hack session was brought to you by Bruno Baptista, Paulo Martins and Andy Gumbrecht, with thanks to Heather VanCura and Devoxx UK 2015.
JCache API
This API allows the use of caching methods in an intuitive and transparent manner. You need two things in your pom.xml
:
The API definition, which defines the JCache interfaces.
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>>1.0.0</version>
</dependency>
An implementation of the API, in this case Hazelcast. Of course you can use any compatible provider.
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-all</artifactId>
<version>3.5</version>
</dependency>
Contexts and Dependency Injection – CDI
Let’s face it, there are plenty of really complicated ways of providing access to application scoped resources. So why on earth would you use them when we have something as cool, and as simple, as CDI? In Java EE 6 we just need to add the beans.xml
file to an application’s META-INF directory and BANG! @Inject @Inject @Inject
…
@Inject
@ObjectCache
private Cache<String, Object> cache;
So where does that come from? Well, although not strictly true, it needs to be produced by a @Producer
…
@Produces
@Singleton
@LocalCacheProvider
public CacheManager createCacheManager() {
We’ve done it this way so that you can see the Magicode and make it easy for you to choose your implementation at runtime.
return Caching
.getCachingProvider("com.hazelcast.cache.impl.HazelcastServerCachingProvider")
.getCacheManager();
Note that there is a second @Producer
in the game here that accepts the CacheManager
in its construction…
@Produces
@Singleton
@ObjectCache
public Cache<String, Object> createUserCache(@LocalCacheProvider final CacheManager cacheManager) {
Here we qualify it as an @ObjectCache
producer, well because it is an Object cache that is being @Produced
. There is nothing to stop you from creating a strongly typed cache for everything and anything. It’s just syntactic sugar; so make it the icing on your cake.
Java EE Interceptors – Pollute nothing, gain everything
Now you have seen how you can add the JCache API everywhere, but do you really want to? Is caching important to your actual business logic? Unlikely at best. Let’s nip this thought in the bud and roll straight on to Java EE Interceptors. We have two scenarios; one is to cache and the other is to invalidate the cache.
@AroundInvoke
public Object cache(final InvocationContext ctx) throws Exception {
final Object[] parameters = ctx.getParameters();
final String key = parameters[0].toString();
Object o = cache.get(key);
if (null == o) {
o = ctx.proceed();
cache.put(key, o);
}
return o;
}
So you can see, this just creates a super simple key using the first parameter of any method you add this interceptor to. If that’s an ID of an @Entity
then it could be just what you need, but again, this is totally up to you to manage. As it happens, our example fits this scenario perfectly. Such a coincidence!
What you should also be able to see is that using this interceptor is actually pretty powerful and keeps your business logic neat and tidy, and completely unaware that there is a cache at all. That is exactly the way it should be!
But now we have our ‘Something’ in the cache. What if some other process changes our ‘Something’? Invalidate the cache, but, in the same vain as above, let’s not pollute our business logic and use another interceptor.
@AroundInvoke
public Object cache(final InvocationContext ctx) throws Exception {
final Object[] parameters = ctx.getParameters();
final String key = parameters[0].toString();
final Object proceed = ctx.proceed();
cache.remove(key);
return proceed;
}
Incredible things from incredible technologies. That’s it, go cache Something today! Oh, hang on, surely we should prove all this Magicode actually works?
Testing Your Magicode with Arquillian
Arquillian compliments all other forms of testing and actually rolls unit, functional and integration testing into one tidy bundle. There is way too much here to explain in a simple blog, so pop over to Arquillian.org to learn more.
Basically, adding the following @RunWith(Arquillian.class)
annotation to your test class will perform some incredible wiring. It will package up your application, fire up a TomEE server, deploy the app, run the test against the deployed app, return the results, and finally, shuts down the TomEE server.
@RunWith(Arquillian.class)
public class InterceptedServiceTest extends Assert {
The test itself is designed to prove that our complex object is cached, updated and invalidated. We could have put the Invalidation interceptor on the update method, but then the test would be less obvious to follow. Give it a try.
What Next? Your own JCache API enabled project.
The world is your oyster on this one. Please feel free to use this code based example as a stepping stone to creating your own JCache API interceptor enabled project – The JCache Annotation API will be added to this project on a future blog. Check out the entire source code for this example project here: https://github.com/tomitribe/JCacheExamples, and don’t forget to have fun!
Hello, I think your site might be having browser compatibility issues.
When I look at your website in Safari, it looks fine but when opening in Internet Explorer, it
has some overlapping. I just wanted to give you
a quick heads up! Other then that, awesome blog!
Ok thank you for the heads up! I will look into it.