Hazelcast is one of the most interesting distributed JCache provider (with Apache Ignite and Infinispan probably) but it doesn't integrate with CDI.

What does it mean? You can, out of the box, do something like:

import javax.cache.annotation.CacheResult;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class CachedService {
    @CacheResult
    public Response someExpensiveLogic(final Request request) {
        // this is long cause we contact 3 remote HTTP servers and use 5 slow databases ;)
        return ...;
    }
}

All the value of previous snippet is in @CacheResult. With that annotation and a CDI friendly JCache provider the > 1 invocations on that method will just read the cache making it very fast. Think one second to a REST service exposing reference data: it just makes the service far more reliable.

However With hazelcast you need to do it programmatically cause it doesn't provide the CDI integration of JCache specification.

Most of other providers are integrated with CDI out of the box so do you need to get rid of hazelcast? Depending your application it can work but if you have an application relying on JCache and some advanced features of hazelcast (so vendor specific API) it would be hard. Other case you don't want to switch is if you already setup a Hazelcast data cluster.

So how to embrace CDI without changing of JCache provider? Just get the CDI integration!

JCS to the rescue

Apache JCS - 2.0-beta-2 released last week - is a JCache implementation but it also has the good idea to deliver the JCache binary in 3 flavors:

  1. default one (JCache + CDI integration)
  2. cdi: just the CDI glue code, not provider specific
  3. nocdi: all but CDI integration (like hazelcast)

Concretely it means you will have in your pom these dependencies:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-jcs-jcache</artifactId>
  <version>2.0-beta-2</version>
  <classifier>cdi</classifier>
</dependency>
<dependency>
  <groupId>com.hazelcast</groupId>
  <artifactId>hazelcast</artifactId>
  <version>3.7.2</version>
</dependency>

And now you run...and it doesn't work. The 2.0-beta-2 had a small (fixed on trunk) bug where the CDI extension was not registered. To solve that just create a file META-INF/services/javax.enterprise.inject.spi.Extension containing:

org.apache.commons.jcs.jcache.cdi.MakeJCacheCDIInterceptorFriendly

Then run again, and it works :).

With hazelcast on a cold JVM (ie not a single request has been done yet) if you test this service:

@CacheResult
public String pauseAndReturn(final String from) {
    try {
        Thread.sleep(1000);
    } catch (final InterruptedException e) {
        Thread.interrupted();
    }
    return from;
}

With this code:

@Test
public void checkCache() {
    final StopWatch watch1 = new StopWatch();
    watch1.start();
    assertEquals("ok", service.pauseAndReturn("ok"));
    watch1.stop();

    final StopWatch watch2 = new StopWatch();
    watch2.start();
    assertEquals("ok", service.pauseAndReturn("ok"));
    watch2.stop();

    System.out.println(watch1.getTime(TimeUnit.MILLISECONDS));
    System.out.println(watch2.getTime(TimeUnit.MILLISECONDS));
}

you will get something like this output:

3781
3

Which shows the cache was used for the second invocation.

Note that the first invocation is "very" slow cause hazelcast needs to start which takes few seconds (2.5s on my machine).

Conclusion

Even if JCS 2.0-beta-2 got a small glitch forgetting the SPI file it is a very low cost for a big gain in term of programming model and using JCache + CDI will give an incredible benefit to your application, in particular for reference data in a microservices architecture. Don't underestimate that, even if the code itself is not hard to develop by yourself, not having to maintain it and being able to configure the cache from outside the application - JCache offers it - is an incredible flexibility for your deployments.

From the same author:

In the same category: