What's the cause of this exception: java.lang.ClassFormatError: Absent Code?

Version 9

Created by Dan Allen on Mar 21, 2010 12:57 AM. Last modified by Dan Allen on May 22, 2012 3:19 PM.

When running a test suite that includes Arquillian test cases, you may be encountering the following exception:

java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file [Fully-qualified class name]
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

This problem is likely the result of having the javax:javeee-api (or javax:javaee-web-api) library on your test classpath. If you have a Maven project, you likely have the following dependency in your POM file:

This dependency provides you with the Java EE APIs, not the implementations. While this dependency works for compiling, it cannot be used for executing code (that includes tests).

Background

When these artifacts were published to the Maven repository, Sun/Oracle stripped out the code from classes that are classified as "implementations". So all the interfaces are code complete, yet any abstract class or implementation class has no code in it. Any attempt to use that class will likely blow up. That's why Arquillian is failing.

Here's an excerpt from the argument as to why Sun/Oracle did this:

When one compiles, they want to run as well. By the way, we have been promoting full set of Java EE APIs which can only be used for compilation - they are stripped off method details. That way, user can't take those artifacts and try to use it in runtime.

Basically, the javax:javaee-api and javax:javaee-web-api Maven artifacts aren’t the real deal -- they’re ‘stubs’. They’re great for easily providing the dependencies necessary for compilation, but they break entirely when you need to do something.

The workaround: do not use the javax.javaee-api or javax:javaee-web-api artifacts! Instead, use the "real" artifacts that you need. Unfortunately, there is no Maven scope available that will exclude a dependency from the test classpath (though there is a classpathDependencyExcludes configuration option in the surefire plugin for blacklisting them during testing).

So how do you use the real artifacts at test runtime? There are two solutions to this problem.

Solution #1: Maven profiles

If you writing tests for an embedded Java EE container, then the container is going to be on your test classpath. That container will have all the APIs you need to compile. Therefore, you use the Java EE API for your default profile, but then replace it with the target container JAR in the profile you are using to run the tests. You can see an example of this setup in the JUnit or TestNG Arquilian example POMs.

Solution #2: JBoss Java EE Spec Artifact

The other solution, which actually applies in both cases, is to use the Java EE spec artifact provided by JBoss, which does not reference any stripped classes.