Today I wanted to introduce Degraph in my project to track cyclic dependencies. This got me a java.lang.IncompatibleClassChangeError which I had never seen before. Here’s a brief report how I solved this.
First, I added Degraph to my Maven-build project by adding this to my pom.xml:
...
<degraph.version> 0.1.2</degraph.version>
...
<dependency>
<groupId> de.schauderhaft.degraph</groupId>
<artifactId> degraph-check</artifactId>
<version> ${degraph.version}</version>
<scope> test</scope>
</dependency>
...
I wrote this short test to make sure that all classes in my project are cycle-free:
import static de . schauderhaft . degraph . check . JLayer .*;
import static de . schauderhaft . degraph . check . JCheck .*;
import static de . schauderhaft . degraph . check . Check . classpath ;
import org.junit.Test ;
import static org . hamcrest . core . Is .*;
import static org . junit . Assert .*;;
public class DependencyTest {
@Test
public void dependencyCyclicality () {
assertThat (
classpath (). noJars (). printTo ( "dependencyCyclicality.graphml" )
. including ( "de.myproject.client.**" )
, is ( violationFree ())
);
}
@Test
public void upwardsDependencies () {
assertThat (
classpath (). noJars (). printTo ( "upwardsDependencies.graphml" )
. including ( "de.myproject.client.**" )
, is ( violationFree ())
);
}
}
Running this test, I got:
java.lang.IncompatibleClassChangeError: class de.schauderhaft.degraph.analysis.asm.GraphBuildingClassVisitor has interface org.objectweb.asm.ClassVisitor as super class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at de.schauderhaft.degraph.analysis.asm.Analyzer$.de$schauderhaft$degraph$analysis$asm$Analyzer$$analyze$1(Analyzer.scala:18)
at de.schauderhaft.degraph.analysis.asm.Analyzer$$anonfun$analyze$1$$anonfun$apply$1.apply(Analyzer.scala:36)
at de.schauderhaft.degraph.analysis.asm.Analyzer$$anonfun$analyze$1$$anonfun$apply$1.apply(Analyzer.scala:35)
at scala.collection.immutable.HashSet$HashSet1.foreach(HashSet.scala:322)
at scala.collection.immutable.HashSet$HashTrieSet.foreach(HashSet.scala:978)
at scala.collection.immutable.HashSet$HashTrieSet.foreach(HashSet.scala:978)
at de.schauderhaft.degraph.analysis.asm.Analyzer$$anonfun$analyze$1.apply(Analyzer.scala:35)
at de.schauderhaft.degraph.analysis.asm.Analyzer$$anonfun$analyze$1.apply(Analyzer.scala:32)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at de.schauderhaft.degraph.analysis.asm.Analyzer$.analyze(Analyzer.scala:32)
at de.schauderhaft.degraph.configuration.Configuration.createGraph(Configuration.scala:31)
at de.schauderhaft.degraph.check.Check$$anon$1.apply(Check.scala:49)
at de.schauderhaft.degraph.check.Check$$anon$1.apply(Check.scala:46)
at de.schauderhaft.degraph.check.hamcrest.HamcrestWrapper.matchesSafely(HamcrestWrapper.scala:8)
at org.hamcrest.TypeSafeMatcher.matches(TypeSafeMatcher.java:65)
at org.hamcrest.core.Is.matches(Is.java:26)
at org.junit.Assert.assertThat(Assert.java:772)
at org.junit.Assert.assertThat(Assert.java:738)
at de.myproject.client.DependencyTest.dependencyCyclicality(DependencyTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
The assumption was that Degraph is apparently being executed against a different version of the asm library than it has been compiled to. To proof this, I had a look at the Maven dependencies. Using IntellijIDEA, I opened the dependency view via the “Maven Projects” view -> rightclick on Maven project -> “Show Dependencies …”.
As you can see, there are two “asm” nodes. Clicking them revealed that the version referenced by Degraph is 5.0.3 where the version referenced by querydsl-jpa is 3.3.1.
I tried updating querydsl-jpa from 2.9.0 to 3.7.3, which worked great:
In the new version, querydsl-jpa has no dependency to asm. This solves the conflict because there is only the one asm version referenced by Degraph. Now all tests work fine.
If the update of querydsl-jpa hadn’t removed the dependency to asm, I would have tried to exclude it using
dependency exclusions . This would work if
qerydsl-jpa
runs fine with the new version of asm provided by Degraph.
TL;DR
When running into an IncompatibleClassChangeError, check your Maven dependencies for multiple occurances of a library. Try to eliminate these.