Mastodon

How to Solve incompatibleclasschangeerror

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 …”.

How to Solve incompatibleclasschangeerror 1

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:

How to Solve incompatibleclasschangeerror 2

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.