Search code examples
javagenericscachingtypesjcache

JCache create cache that holds a list of items per key (Generics)


I want to have a cache that holds a list of items. This is my bean definition:

public Cache<Integer, List<Application>> getMyCacheManager(CacheManager manager) {
    Cache<Integer, List<Application>> cache = manager.getCache("myCache");
    if (Objects.isNull(cache)) {
        var config = new MutableConfiguration<Integer, List<Application>>()
                .setTypes(Integer.class, List.class)
                .setStoreByValue(false)
                .setStatisticsEnabled(true)
                .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, myCacheExpiry)));
        cache = manager.createCache("myCache", config);
    }
    return cache;
}

The problem is the .setTypes line. Here you have to specify the key and value types of the cache. The key is straight forward, but the value is a List<Application> type. You can't just say: List<Application>.class, because that is not allowed. And you also can't use List.class, because the compiler complaints the types are not compatible.

I would really like to keep the specific type (eg List<Application>) because else I have to cast the value every time from Object to the right type.

Any ideas?


Solution

  • The JSR107/JCache specification tried to incorporate typing in the configuration. With collection types this leads into confusion like you have.

    Unfortunately, you need to add a cast to the correct types you want to use. A direct cast is forbidden by the compiler, so you need to cast to Object or just Cache first.

    public Cache<Integer, List<Application>> getMyCacheManager(CacheManager manager) {
        Cache<Integer, List<Application>> cache = manager.getCache("myCache");
        if (Objects.isNull(cache)) {
            var config = new MutableConfiguration<Integer, List<Application>>()
                    .setTypes(Integer.class, List.class)
                    .setStoreByValue(false)
                    .setStatisticsEnabled(true)
                    .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, myCacheExpiry)));
            cache = (Cache<Integer, List<Application>>) ((Object) manager.createCache("myCache", config));
        }
        return cache;
    }
    

    Caches might check the types or exploit the type information you give to do some optimizations. E.g. cache2k would optimize for integer keys and store keys as primitive types, not objects.

    Explicit type checking inside the cache is not part of the JSR107 specification. That means, with different JCache implementations you may or may not store any type of object regardless what you specified in the configuration. Typically, a store by reference cache (setStoreByValue(false)), stores any object reference without additional checks. The latter would be just unnecessary overhead.

    Portions of the JSR107 do speak of runtime type checking. This was intended to ensure that type compatible caches are returned by the cache manager, not to enforce that only the specified typed objects go into a cache. Strict type checking was dropped in a service release, because it proved impractical. The relevant discussion is at: https://github.com/jsr107/jsr107spec/issues/340