Close
Back

JCache, why and how?

Like the small Java Specification Request (JSR) number 107 suggests, JCache was a very long standing standardization effort that finally finished last year - over ten years after it got started. What does it mean to you? Why, where and how could you use it?

Caching libraries in the Java ecosystem are almost as common as mosquitoes in Northern Europe. Ehcache, Hazelcast, Infinispan, GridGain, Apache Ignite, JCS… Caching is needed in many different kinds of solutions to optimize the application in various ways. The simplest libraries are just in-memory object maps with simple evict rules, while the most advanced caching libraries have efficient cross-JVM features and configurable options to write the cache state to disk, making them cluster ready persistency options as such, and a good basis for memory heavy computation and big data style programming.

Most caching solutions are based on map like data structures and JCache API tries to standardize the most common use cases. If you have advanced needs, you probably have to use some implementation specific features, like with JPA, but the standard will definitely make it easier to swap between caching libraries in the future. And it also makes it easier for developers to move from a project to another, which are probably using different caching libraries.

“Simple” usage

Although the default JCache API doesn’t let you adjust all implementation specific features, the default API is pretty versatile and should adapt to most common use cases. In the following code snippet, you can get an overview of creating and configuring a cache and using it to cache an expensive service method call.

private void listEntriesJavaSEStyle(String filter) {
   final String name = "myCache";

    Cache<String, List> cache = Caching.getCache(name, String.class,
            List.class);
    if (cache == null) {
        final CachingProvider cachingProvider = Caching.getCachingProvider();
        final CacheManager mgr = cachingProvider.getCacheManager();
        MutableConfiguration<String, List> config = new MutableConfiguration<>();
        config.setTypes(String.class, List.class);
        config.setStoreByValue(true);
        config.setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(
                Duration.ONE_MINUTE));
        Notification.show("Creating cache",
                Notification.Type.WARNING_MESSAGE);
        cache = mgr.createCache(name, config);
    }

    // first look up from cache, if not found, go to service and cache value
    List cached = cache.get(filter);
    if (cached != null) {
        Notification.show("Cache hit!");
        entryList.setBeans(cached);
    } else {
        Notification.show("Cache missed :-(");
        List<PhoneBookEntry> entries = service.getEntries(filter);
        cache.put(filter, entries);
        entryList.setBeans(entries);
    }
}

The code snippet uses the filter string as a key for the cache and caches the result list from backend in the cache, if not found. In serious usage, you’d naturally move configuring the cache away from you application logic and save the cache reference to a local field.

The cache configuration is the most probable place where you’ll still need to enter implementation specific code or other configuration options. The standard API has, for example, quite limited evict rule configuration possibilities, supporting only simple expiration time based rules.

The actual Cache object API is straightforward for developers with experience of java.util.Map usage or proprietary caching APIs. I’d expect it to match most caching use cases as such, and the need to escape to implementation specific APIs during actual usage will be rare.

The JCache API also supports cache mutation events and entry processors. When using those, you should however notice that JCache doesn’t specify that they should be run in the same JVM process as your own code. So using them for stuff that doesn’t relate directly to the cache may become trickier than you think, especially in a clustered environment. For example modifying your Vaadin UI(s) from a CacheEntryListener might be unstable on certain implementations.

Managed bean goodies

The javax.cache.annotation package contains a lot of handy looking annotations. With them you can implement a common caching logic for managed beans (EJB, CDI, Spring…) just by adding a single annotation to a method. For example, the above example to cache the possibly expensive backend call can be implemented as such:

@CacheResult
public List<PhoneBookEntry> getEntries(String filter) {
    // Same very same business logic as in a version without caching
}

The CacheResult annotation is all that is needed and the container will then handle everything, including setting up the cache, checking for existing values from the cache and storing the value, if the value was not found from the cache. The actual cache name can be explicitly defined in the annotation, as well, or at class level using the CacheDefaults annotation. I’d say that is really simple caching and that’s why I used quotation marks in the previous sub-title.

Super handy, but the downside is that annotations are not that well supported yet. Those who are using Spring 4.1 or newer are super lucky to enjoy these goodies today. Also, the latest Payara (a commercially supported build of GlassFish) has a built-in support for JCache, including the annotations, using Hazelcast as an implementation. The good folks at Tomitribe have put together a nice CDI extension library that allows your to use these annotations in any CDI managed bean today.

Summary

If you are already using a specific caching library in your project, I don’t see that much value in throwing in JCache API eagerly today. Also, if your cache library is used by another library, like as a second level cache for your JPA implementation, the JCache doesn’t bring any advantage to you. But you should definitely start using it in new projects with basic caching requirements and especially in projects where you are planning to try different libraries. All major caching libraries already support JSR 107.

All in all, I think it hasn’t really been an issue that the JCache API took ages to standardize. Portability of applications has been one of the main goals, but hasn’t been that big of an issue on this area. The usage of various caching libraries has always been rather similar (~ java.util.Map like API) and it has never been a huge task to swap between caching libraries. The largest differences have been in the way caches have been configured. Setting up a cache can now be defined with a common API as well, but I’d guess that this is an area where many users will still have to fall back to implementation specific features. In cache configuration, with feature packed implementations, a common standard just cannot tackle everything.

The actual cache usage of caching libraries will, in the future, mostly use the exact same API, independently of the library in use. This will increase a healthy competition among various caching solutions, but the largest benefit of the JSR 107 will be the added productivity of Java developers, who can in the future use the very same API for caching, in each and every project they work in.

The declarative annotation based “caching hints” for managed beans is a feature I expect to be really popular in the future. You can find example usages of the declarative approach and the raw Java API usage from my example project that uses a CDI based service class with Vaadin UI. As an implementation I used Hazelcast, the first library that published support for the final JCache API, and the handy JCache-CDI library by Tomitribe.

Check out the JCache with Vaadin example

Learn more about Vaadin, the easiest way to build web apps in Java

Comments
Add Comment
pls upload some example simple vanilla project(non spring, non-hibernate) for caching of methods using annotation.
Posted on 7/23/15 12:32 PM.
The linked example project is "vanilla Java EE 6" project. There is no Spring, Hibernate nor even JPA.
Posted on 7/27/15 10:02 AM in reply to akansha sri.