0

My experience with Java class loading is limited. With tools like Maven I have a rusty understanding of how they resolve dependency versions. But I've hit a problem that's making me question how Java loads classes.

My scenario, I have a dependency on version 30.1.1-jre of com.google.guava. I also have a shaded jar which has a dependency on Guava 18.0.

In my application I end up seeing the following exception

java.lang.IncompatibleClassChangeError: Class com.google.common.base.Suppliers$MemoizingSupplier does not implement the requested interface java.util.function.Supplier

which I cannot reproduce locally. Based on https://github.com/crabhi/celery-java/issues/9 it sounds like this error is produced when an older version of Guava is on the classpath.

Checking the classes in the war I see

WEB-INF/lib/java-driver-shaded-guava-25.1-jre-graal-sub-1.jar.d/com/datastax/oss/driver/shaded/guava/common/base/Suppliers$MemoizingSupplier.class

WEB-INF/lib/nautilus-es2-library-2.3.4.jar.d/com/google/common/base/Suppliers$MemoizingSupplier.class

WEB-INF/lib/guava-30.1.1-jre.jar.d/com/google/common/base/Suppliers$MemoizingSupplier.class

This makes me think the shaded jars are causing a problem.

Is that possible? Are there any articles explaining how classes are loaded when shaded jars enter the picture?

Shane Gannon
  • 6,770
  • 7
  • 41
  • 64
  • not really class loader related. Shaded means that the package name of some classes have been altered from like com.google.common to e.g. shaded.com.google.common so they can exist side by side to the "same" library. Are the dependency in your shaded jar included and shaded inside https://maven.apache.org/plugins/maven-shade-plugin/examples/includes-excludes.html ? – zapl Nov 22 '22 at 21:15
  • I've assumed so, but I'll need to give that a closer loo. This is useful direction BTW. – Shane Gannon Nov 22 '22 at 21:39
  • Added examples of where `MemoizingSupplier` is defined in the war – Shane Gannon Nov 23 '22 at 17:04

1 Answers1

0

You have two copies of com.google.common.base.Suppliers.MemoizingSupplier on the classpath. Only one can be loaded, and in your case, it's the older version.

You shouldn't have multiple classes with the same name available to one classloader. What is nautilus-es2-library-2.3.4.jar and why does it bundle Guava rather than expressing it as a transitive dependency?

erickson
  • 265,237
  • 58
  • 395
  • 493
  • `nautilus-es2-library-2.3.4.jar` is a shaded jar containing the `ElasticSearch 2.3.4` client code and its dependencies. My knowledge of shaded jars is limited but I thought the purpose was to bundle all dependencies within a single jar. I also assume, thought I have not confirmed, that `guava-30.1.1` would not be compatible with ES2.3.4. – Shane Gannon Nov 24 '22 at 17:52
  • 1
    @ShaneGannon you can do 2 things with jars, simply bundle dependencies into a single big jar and shading (renaming/relocating) them while doing so. The latter requires changes to both the package name of the class that gets relocated as well to all the imports in all places that use that class. Can you modify / replace `nautilus-es2-library-2.3.4.jar` because it's not doing the shading part, it simply bundles guava. There's https://github.com/desp0916/es-shaded which might be a way to build your own shaded version of elasticsearch and that one defines relocations for guava in it's pom – zapl Nov 25 '22 at 00:48
  • @zapl thank you, again, for the direction. `relocating` this specific jar in the shaded library is the solution. i.e. https://maven.apache.org/plugins/maven-shade-plugin/examples/class-relocation.html. Thank you for pointing this out. I was focusing too much on how the classes were loaded. I still don't have a good understanding of that, but this approach was the right solution. – Shane Gannon Nov 25 '22 at 19:44