New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Questions - CDI Dependency and Disable Caching for testing with in-memory db #151

Closed
charlesxucheng opened this Issue Apr 20, 2017 · 5 comments

Comments

Projects
None yet
2 participants
@charlesxucheng

charlesxucheng commented Apr 20, 2017

Thanks for the great work.

I am using Caffeine JCache provider with Play in Scala with Guice as the DI framework. Two things I would like to clarify:

  1. If I put the following dependencies:
libraryDependencies ++= Seq(
  "com.github.ben-manes.caffeine" % "caffeine" % "2.4.0",
  "com.github.ben-manes.caffeine" % "jcache" % "2.4.0",
  "org.jsr107.ri" % "cache-annotations-ri-guice" % "1.0.0"
)

I get a compilation error:
Class javax.enterprise.util.Nonbinding not found - continuing with a stub.

I have to add "org.jsr107.ri" % "cache-annotations-ri-cdi" % "1.0.0" or "javax.enterprise" % "cdi-api" % "2.0-PFD2" for the compilation to work. Is this expected, or is there a missing dependency somewhere?

  1. For production code, I bind Caffeine objects as follows:
  def configureCachingProvider() = {
    val provider = Caching.getCachingProvider(classOf[CaffeineCachingProvider].getName)
    val cacheManager = provider.getCacheManager(provider.getDefaultURI, provider.getDefaultClassLoader)
    bind[CacheResolverFactory].toInstance(new DefaultCacheResolverFactory(cacheManager))
    bind[CacheManager].toInstance(cacheManager)
  }

For testing, I don't want to enable Caffeine, because I am using some in-memory database, and enabling caching may affect some of the test results. What I did is this in my test module.

    bind[CacheManager]toInstance(Caching.getCachingProvider.getCacheManager)

Somehow this works, but I am not sure what CacheManager is returned by the above. Is there a default implementation provided by the reference implementation?

Thanks for your patience.

@ben-manes

This comment has been minimized.

Show comment
Hide comment
@ben-manes

ben-manes Apr 20, 2017

Owner

The Guice RI should work. Scala requires annotations to fully resolve, whereas Java ignores them unless reflective resolved, so there may be an ri compile time dependency you have to promote to runtime. I'd check the ri pom.

This Guice test might be helpful.

JCache uses a ServiceLoader to discover the implementation. In your test, it's resolving to Caffeine again.

I'd be weary of jcache outside of framework integrations. It has a brittle, limited api with lots of quirks. I don't think it was a positive step forward.

Owner

ben-manes commented Apr 20, 2017

The Guice RI should work. Scala requires annotations to fully resolve, whereas Java ignores them unless reflective resolved, so there may be an ri compile time dependency you have to promote to runtime. I'd check the ri pom.

This Guice test might be helpful.

JCache uses a ServiceLoader to discover the implementation. In your test, it's resolving to Caffeine again.

I'd be weary of jcache outside of framework integrations. It has a brittle, limited api with lots of quirks. I don't think it was a positive step forward.

@ben-manes

This comment has been minimized.

Show comment
Hide comment
@ben-manes

ben-manes Apr 20, 2017

Owner

It looks like javax.enterprise.util.Nonbinding is a CDI annotation and I don't see it used in the JCache RI repository. It does use other javax.enterprise imports and has a dependency. So this shouldn't be needed for Guice.

Owner

ben-manes commented Apr 20, 2017

It looks like javax.enterprise.util.Nonbinding is a CDI annotation and I don't see it used in the JCache RI repository. It does use other javax.enterprise imports and has a dependency. So this shouldn't be needed for Guice.

@charlesxucheng

This comment has been minimized.

Show comment
Hide comment
@charlesxucheng

charlesxucheng Apr 21, 2017

Thanks for the fast response.

I did a dependency check and found javax.cache.annotation.CacheResult annotation uses javax.enterprise.util.Nonbinding

But javax.enterprise.util.Nonbinding is not in the dependency chain.

[info] +-com.github.ben-manes.caffeine:caffeine:2.4.0
[info] +-com.github.ben-manes.caffeine:jcache:2.4.0
[info] | +-com.github.ben-manes.caffeine:caffeine:2.4.0
[info] | +-com.typesafe:config:1.3.1
[info] | +-javax.cache:cache-api:1.0.0
[info] | +-javax.inject:javax.inject:1

[info] +-org.jsr107.ri:cache-annotations-ri-guice:1.0.0
[info] | +-com.google.inject:guice:3.0 (evicted by: 4.1.0)
[info] | +-com.google.inject:guice:4.1.0
[info] | | +-aopalliance:aopalliance:1.0
[info] | | +-com.google.guava:guava:19.0 (evicted by: 20.0)
[info] | | +-com.google.guava:guava:20.0
[info] | | +-javax.inject:javax.inject:1
[info] | |
[info] | +-javax.cache:cache-api:1.0.0
[info] | +-org.jsr107.ri:cache-annotations-ri-common:1.0.0
[info] | +-javax.cache:cache-api:1.0.0

This Guice test might be helpful.

I think this is where I get the configureCachingProvider code from... However, what I need is to plug in a dummy implementation. I guess I can write one myself...

I'd be weary of jcache outside of framework integrations. It has a brittle, limited api with lots of quirks. I don't think it was a positive step forward.

You mean it is not recommended to use JCache annotations outside of frameworks? I thought using such annotations is an easy way to enable caching instead of doing all the explicit gets and puts in the code... If not then what is a good way to enable caching without polluting my code with such cross-cutting concern?

Thanks!

charlesxucheng commented Apr 21, 2017

Thanks for the fast response.

I did a dependency check and found javax.cache.annotation.CacheResult annotation uses javax.enterprise.util.Nonbinding

But javax.enterprise.util.Nonbinding is not in the dependency chain.

[info] +-com.github.ben-manes.caffeine:caffeine:2.4.0
[info] +-com.github.ben-manes.caffeine:jcache:2.4.0
[info] | +-com.github.ben-manes.caffeine:caffeine:2.4.0
[info] | +-com.typesafe:config:1.3.1
[info] | +-javax.cache:cache-api:1.0.0
[info] | +-javax.inject:javax.inject:1

[info] +-org.jsr107.ri:cache-annotations-ri-guice:1.0.0
[info] | +-com.google.inject:guice:3.0 (evicted by: 4.1.0)
[info] | +-com.google.inject:guice:4.1.0
[info] | | +-aopalliance:aopalliance:1.0
[info] | | +-com.google.guava:guava:19.0 (evicted by: 20.0)
[info] | | +-com.google.guava:guava:20.0
[info] | | +-javax.inject:javax.inject:1
[info] | |
[info] | +-javax.cache:cache-api:1.0.0
[info] | +-org.jsr107.ri:cache-annotations-ri-common:1.0.0
[info] | +-javax.cache:cache-api:1.0.0

This Guice test might be helpful.

I think this is where I get the configureCachingProvider code from... However, what I need is to plug in a dummy implementation. I guess I can write one myself...

I'd be weary of jcache outside of framework integrations. It has a brittle, limited api with lots of quirks. I don't think it was a positive step forward.

You mean it is not recommended to use JCache annotations outside of frameworks? I thought using such annotations is an easy way to enable caching instead of doing all the explicit gets and puts in the code... If not then what is a good way to enable caching without polluting my code with such cross-cutting concern?

Thanks!

@ben-manes

This comment has been minimized.

Show comment
Hide comment
@ben-manes

ben-manes Apr 21, 2017

Owner

Nice find. In their POM it states,

<!--This is only needed if you are using a CDI based implementation of the annotations support.
    In CDI 1.1. we should be able to remove this dependency completely. -->
<dependency>
  <groupId>javax.enterprise</groupId>
  <artifactId>cdi-api</artifactId>
  <version>1.0-SP4</version>
  <scope>provided</scope>
  <optional>true</optional>
  ...
</dependency>

You could request that they remove the dependency usage since CDI 1.1 was released 4 years ago.

I think this is where I get the configureCachingProvider code from... However, what I need is to plug in a dummy implementation. I guess I can write one myself...

The configureCachingProvider was only needed because my suite has other caching libraries which also implement JCache. If you have only one, then you can probably don't need it except to override to a mock in testing code.

You mean it is not recommended to use JCache annotations outside of frameworks? I thought using such annotations is an easy way to enable caching instead of doing all the explicit gets and puts in the code... If not then what is a good way to enable caching without polluting my code with such cross-cutting concern?

It is my opinion that JCache does more harm than good, but you are welcome to disagree. The only value I see is to ease framework integrations, but it is highly flawed for application developers.

The annotations are quirky and interact poorly with the rest of JCache. For example,

  • CacheResult is a racy get-compute-put operation. Multiple concurrent calls for the same key will be executed, the value stampeded over each other, and finally quiesce. This is known as a cache stampede and a proper implementation would perform it atomically.
  • CacheResult does not support bulk loads, e.g. cache.getAll, which can be a useful optimization.
  • The annotations are mostly incompatible with CacheLoader and CacheWriter due to using a private key wrapper.

You'll discover more of these quirks as you become familiar with JCache and your use cases become modestly complex. You might then prefer to use Caffeine's apis directly which are fairly succinct and, hopefully, provide the right escape hatches to not get in your way. But you can definitely start with JCache, evaluate, and migrate if needed.

Owner

ben-manes commented Apr 21, 2017

Nice find. In their POM it states,

<!--This is only needed if you are using a CDI based implementation of the annotations support.
    In CDI 1.1. we should be able to remove this dependency completely. -->
<dependency>
  <groupId>javax.enterprise</groupId>
  <artifactId>cdi-api</artifactId>
  <version>1.0-SP4</version>
  <scope>provided</scope>
  <optional>true</optional>
  ...
</dependency>

You could request that they remove the dependency usage since CDI 1.1 was released 4 years ago.

I think this is where I get the configureCachingProvider code from... However, what I need is to plug in a dummy implementation. I guess I can write one myself...

The configureCachingProvider was only needed because my suite has other caching libraries which also implement JCache. If you have only one, then you can probably don't need it except to override to a mock in testing code.

You mean it is not recommended to use JCache annotations outside of frameworks? I thought using such annotations is an easy way to enable caching instead of doing all the explicit gets and puts in the code... If not then what is a good way to enable caching without polluting my code with such cross-cutting concern?

It is my opinion that JCache does more harm than good, but you are welcome to disagree. The only value I see is to ease framework integrations, but it is highly flawed for application developers.

The annotations are quirky and interact poorly with the rest of JCache. For example,

  • CacheResult is a racy get-compute-put operation. Multiple concurrent calls for the same key will be executed, the value stampeded over each other, and finally quiesce. This is known as a cache stampede and a proper implementation would perform it atomically.
  • CacheResult does not support bulk loads, e.g. cache.getAll, which can be a useful optimization.
  • The annotations are mostly incompatible with CacheLoader and CacheWriter due to using a private key wrapper.

You'll discover more of these quirks as you become familiar with JCache and your use cases become modestly complex. You might then prefer to use Caffeine's apis directly which are fairly succinct and, hopefully, provide the right escape hatches to not get in your way. But you can definitely start with JCache, evaluate, and migrate if needed.

@charlesxucheng

This comment has been minimized.

Show comment
Hide comment
@charlesxucheng

charlesxucheng Apr 21, 2017

Thanks Ben. I will explore what you have suggested.

charlesxucheng commented Apr 21, 2017

Thanks Ben. I will explore what you have suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment