Tests execution failed after upgrading from gradle -1.3 to 1.6 or 1.7. Error message java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/ServletInputStream

Tests execution failed after upgrading from gradle -1.3 to 1.6 or 1.7. Error message java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/ServletInputStream

I have the the same dependencies, the same build script. If I run with Gradle 1.3, it still works. But with Gradle 1.7 I got above mentioned error. My test dependencies: estCompile(group: ‘org.springframework’, name: ‘spring-test’, version: ‘3.0.5.RELEASE’)

testCompile(group: ‘org.glassfish.extras’, name: ‘glassfish-embedded-all’, version: ‘3.1.1’)

testCompile(group: ‘commons-dbcp’, name: ‘commons-dbcp’, version: ‘1.4’)

testCompile(group: ‘cglib’, name: ‘cglib’, version: ‘2.2’)

testCompile(group: ‘org.glassfish’, name:‘javax.servlet’,version:‘3.0’)

What else I should check?

To start out, the full error message and full stack trace (–stacktrace) would help. Also make sure you do a clean build and delete the .gradle directory.

I also get this problem. In my case, it’s not related to a version upgrade - it fails with both 1.2 and 1.5. Here is a tiny project demonstrating the problem - try running ‘gradle test’:

https://bitbucket.org/twic/gradleclasspathorderissue

Essentially, this is a project with a compile dependency on the JavaEE API jar from Sun/Oracle, and a test runtime dependency on the JavaEE specs jars from JBoss. This is a quite common arrangement: the official API jar contains deliberately broken class files that can be used to compile against, but cannot be used at runtime, eg in unit tests, so an alternative, non-broken, implementation of the API is used to run the tests.

The crucial thing here is that both dependencies define the same set of classes (or at least, an overlapping set). We require the dependency defined in testRuntime to override the one defined in compile. It seems that this does not happen.

Note that if you remove the compile dependency from this little project, the tests pass, because now they link against the testRuntime dependency. Obviously, this is not practical in a real project, where the compile dependency is needed to compile code!

It would be possible to use the testRuntime dependency as a compile dependency as well, but this is not desirable, because it would tie the built artifact to a particular non-official implementation of the API. It could be done as a providedCompile dependency to avoid this, but this only works for WAR projects, and loses the dependency from the artifact altogether, which is also bad.

The fix for this issue, i believe, is to ensure that jars which are testRuntime dependencies (direct or transitive) should take precedence over those which are compile dependencies.

I’m happy to raise a bug for this, if that would be more useful.

If you put the JavaEE API Jar on the ‘compile’ configuration, it will make it to the ‘runtime’ configuration and to depending projects’ configurations. Instead (and until Gradle provides an out-of-the-box solution), it’s better to declare your own ‘provided’ configuration. If you need more information, I recommend to search the forums or Stack Overflow for ‘provided’ (in the context of Gradle).

Thanks for the advice.

However, if possible, i would like to keep the normal compile dependency on the official JavaEE API jar. After all, it is the single canonical representation of that API in Maven module space. I think it’s entirely appropriate that it becomes a transitive dependency of depending projects.

The only problem is that it makes unit tests impossible! Accordingly, i would rather deal with the problems around unit testing as part of the testRuntime configuration. The solution i have hit on is to exclude it:

configurations {
 testRuntime.exclude group: 'javax', module: 'javaee-api'
}

After which everything works fine.

Is there any reason not to use this solution?

1 Like

It means that everyone who depends on your Jar will have to include this line, and may also have to add an exclude for ‘runtime’ (otherwise ‘javaee-api’ may end up in a War/Ear).