5

I intentionally have a couple of different caching providers in my classpath. I have Hazelcast for distributed cache and Caffeine for a local cache. I am trying to use JCache (JSR107) annotations for caching my values.

I have already created a CacheResolverFactory that will be able to detect which cache manager to use from which provider (based on method annotations), but when I launch my application I get the following error message:

Exception in thread "Thread-2" javax.cache.CacheException: Multiple CachingProviders have been configured when only a single CachingProvider is expected
    at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:386)
    at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:361)
    at javax.cache.Caching.getCachingProvider(Caching.java:151)
    at org.jsr107.ri.annotations.DefaultCacheResolverFactory.<init>(DefaultCacheResolverFactory.java:59)
    at org.jsr107.ri.annotations.cdi.CacheLookupUtil.<init>(CacheLookupUtil.java:45)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

Clearly, I am aware that I have multiple cache providers. However, I cannot seem to find anyway around this issue. The org.jsr107.ri.annotations.cdi.CacheLookupUtil class has a private CacheResolverFactory member which is initialized on construction to the DefaultCacheResolverFactory() which expects a single CacheProvider.

public class CacheLookupUtil extends AbstractCacheLookupUtil<InvocationContext> {
    @Inject
    private BeanManagerUtil beanManagerUtil;
    private CacheKeyGenerator defaultCacheKeyGenerator = new DefaultCacheKeyGenerator();
    private CacheResolverFactory defaultCacheResolverFactory = new DefaultCacheResolverFactory();
...
...
}

Is there anything I can do to circumvent this problem? Can JCache ONLY be used with 1 caching provider? And the CacheLookupUtil class is field @Injected into all the annotation processors.

Is my only choice to override all the interceptors (and provide custom interceptors) and create my own CacheLookupUtil implementation? Or is there something else I can do instead?

Eric B.
  • 23,425
  • 50
  • 169
  • 316

1 Answers1

1

JCache supports multiple caching providers.

You need to activate your custom cache provider for the annotation processor. Like this:

@CacheDefaults(cacheResolverFactory=LocalCacheResolver.class)
public class SomeService {
  // methods with caching annotations
}

Note about the RI annotations: I have mixed feelings about the usage. In general its part of the reference implementation of the JCache standard. The whole JCache reference implementation (RI) isn't intended to be used in production and was created to validate the standard. Using the annotation processing of the RI was propagated here by Greg Luck back in 2014, see: How JSR107 Caching Annotations are meant to be used. However, that was meant as an intermediate solution before relevant other implementations become available.

cruftex
  • 5,545
  • 2
  • 20
  • 36
  • My goal was to provide my own implementation of the DefaultCacheResolverFactory such that I didn't need to specify it everytime. Even if I do specify the resolver in the annotation, it still fails, because the interceptors require a CacheLookupUtil bean to be injected. Unfortunately the RI CacheLookupUtil bean initializes the defaultCacheResolverFactory at instantiation time (not with injection) and the RI version does not seem to support >1 providers. – Eric B. Aug 05 '19 at 21:44
  • I looked for other implementations of the RI annotations, but haven't been able to find any implementations other than the RI for annotation support. Everything I find indicates to use the CDI RI annotation interceptors. Do you know of any other implementations that I can use for CDI annotation support? – Eric B. Aug 05 '19 at 21:48
  • The `DefaultCacheResolverFactory` has the semantics as defined in the standard. Providing you own defaults, although a useful feature, isn't part of the standard. So the actual problem you or many others run into is, that as soon as you have more then one caching provider, you need to declare everywhere which one to use. That's painful. If you have more then one provider, it is possible to set a system property `javax.cache.cpi.CachingProvider` which instructs the provider factory which provider should be returned as default. – cruftex Aug 06 '19 at 06:08
  • I did a review of the JCache code around the semantics `javax.cache.cpi.CachingProvider`. It looks like it doesn't work as intended, as soon as you request a different provider then the default. I will double check that and open a discussion in the JSR107 maintenance group. – cruftex Aug 06 '19 at 06:11
  • JCache annotations: AFAIK, Spring has its own implementation. – cruftex Aug 06 '19 at 06:18
  • There are several flaws in JCache annotations, not only this one. The major one that I remember is that you cannot use any object as cache key directly, but always need a wrapper. – cruftex Aug 06 '19 at 06:20
  • Indeed, I noticed that with the key wrapper already as well, but something that was able to workaround at least. The multiple cache providers leaves me a little stuck. I have no idea how to workaround the issue without rewriting the annotations jar. Which I'm trying to avoid as it means more code that has to be maintained, and will add complexity to an already complicated codebase. Can you think of any other mechanisms that would allow me to hack around the issue? Had the classes used constructor based initialization, I could have subclasses, but private members leaves me a little stuck. – Eric B. Aug 06 '19 at 12:26