Adam Bien's Weblog

Wednesday Oct 20, 2010

Trouble With Crippled Java EE 6 APIs in Maven Repository And The Solution

If you try to load the javax.persistence.EntityManager class coming from standard java.net Repository you will get the following Exception:


java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/persistence/LockModeType
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)


Loading a class is needed for mocking, so the following code will not run:

@Stateless
public class EJB3WithEntityManager {
    @PersistenceContext
    EntityManager em;
    
    public void save(AnEntity ae){
        em.persist(ae);
    }
}

public class EJB3WithEntityManagerTest {
    private EJB3WithEntityManager cut;
    @Before
    public void injectEntityManager(){
        this.cut = new EJB3WithEntityManager();
        this.cut.em = mock(EntityManager.class);
    }
    @Test
    public void testSomeMethod() {
        AnEntity ae = new AnEntity();
        this.cut.save(ae);
        verify(this.cut.em,times(1)).persist(ae);
    }
}

Instead of using
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>6.0</version>
    <scope>provided</scope>
</dependency>

You should use alternative (geronimo, jboss etc.) dependencies:

<dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-ejb_3.1_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>
   <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-jpa_2.0_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>

I cannot imagine any reasonable motivation behind removing the implementation of API-classes before uploading them into central maven repository, except political or licensing issues. This approach, however, has one advantage. You can tell whether a project is actually unit tested, or not, just by looking at the POM :-).
The whole example with workaround was checked into http://kenai.com/projects/javaee-patterns The name of the project is: MavenUnitTestWithCrippledAPI.


NEW: Java EE 7 Testing and Quality Workshop

A book about rethinking Java EE Patterns

Comments:

You don't have to swap the dependencies - it's enought to have the implementations in "test" scope *before* the API dependency:

e.g. Eclipselink for an outside-of-container test of a glassfish project:

<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc5</artifactId>
<version>11.2.0.1.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<scope>provided</scope>
<version>6.0</version>
</dependency>

Posted by Matthias Fraass on October 20, 2010 at 03:53 PM CEST #

@Matthias,

but why? They are "provided" anyway. Just going with one set of dependencies makes the POM shorter. javaee-web-api is the WEB-API, and the other dependencies are the API with implementation.

thanks!,

adam

Posted by adam-bien.com on October 20, 2010 at 04:10 PM CEST #

Hi, you also could use different profiles.

The test profile could then use the implementation-jar.

The profile for generating the product could use the api-jar.

Posted by Stefan Bohn on October 20, 2010 at 05:53 PM CEST #

you are right, you can omit that.

Posted by Matthias Fraass on October 20, 2010 at 06:58 PM CEST #

@Stefan,

sure. Instead of using the bogus Java EE 6 class files, I would just use the "real" implementations and not refer to the Java EE 6 API at all.

But in general - you are right :-)
The real problem is the bogus JAR in maven repo. Our ideas are just workarounds :-)

thanks for your thoughts!,

adam

Posted by adam-bien.com on October 20, 2010 at 07:07 PM CEST #

It was my understanding that the reason they are not in the central repo is licensing issues. :(

Posted by jkilgrow on October 20, 2010 at 08:49 PM CEST #

@jkilgrow,

could be. I guess even warranty claims or something like that. The problem are a few abstract classes with removed implementation. They could be implemented from scratch without any implementation in hours if not minutes.

I guess it would solve several millions of man hours worldwide :-)

thanks!,

adam

Posted by adam-bien.com on October 20, 2010 at 11:28 PM CEST #

there is other solution if you are using maven >=2.0.9

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.0.CR2</version>

<scope>test</scope>

</dependency>

<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>

more information:

http://stackoverflow.com/questions/2116220/how-can-i-use-different-jars-for-compiling-and-testing-in-maven

Posted by 89.72.75.239 on October 21, 2010 at 12:50 AM CEST #

@89.72.75.239,

(you have a geeky name :-)).
Sure - it is identical to the @Matthias suggestion.
But I would just use Hibernate, without javaee-web-api then.

thanks for the link,

adam

Posted by adam-bien.com on October 21, 2010 at 01:07 AM CEST #

I think it's not licensing nor warranty concerns (that missing code is trivial). I think it's just that it's not part of the spec! The implementers could choose to do some specific stuff in there... but your unit tests should not depend on those.

Instead of removing the code, it would be nicer to have all methods throw an exception instead, wouldn't it?

But I wonder why you're having problems with the EntityManager. That is an interface, so there should be no code block missing and the mocking should work!

Rüdiger

Posted by Rüdiger on October 21, 2010 at 04:14 PM CEST #

Wasn't one of the goals of Gemini to expose Java EE 6 and related artifacts in a better way ?;-)

JSR-330 is another hilarious example.

After PFD-1
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1.0-PFD-1</version>
</dependency>
which caused a whole bunch of online flames especially from EE stakeholders
it ended up as this:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>

Quick and Dirty and a bit love-less I'd say, others in the JCP call that "Agile" ;-|

Posted by Werner Keil on February 06, 2011 at 05:40 PM CET #

Other way is to use:

<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1</version>
<scope>provided</scope>
</dependency>

with

<repository>
<id>java.net</id>
<name>GlassFish Maven Repository</name>
<url>http://download.java.net/maven/glassfish</url>
</repository>

Then you have all dependencies including javax.validation.

Posted by Przemysław Pelczar on July 18, 2011 at 03:48 PM CEST #

Hi all,

I found a nice workaround on Arquillian wiki (https://community.jboss.org/wiki/WhatsTheCauseOfThisExceptionJavalangClassFormatErrorAbsentCode). It seems to work for me, even with non-Arquillian tests.

<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<classpathDependencyExcludes>
<!-- exclude code absent api -->
<classpathDependencyExclude>javax:javaee-api</classpathDependencyExclude>
<classpathDependencyExclude>javax:javaee-web-api</classpathDependencyExclude>
</classpathDependencyExcludes>
</configuration>
</plugin>

Posted by Alexis on April 03, 2012 at 08:51 AM CEST #

Another ugly thing is that these javax API classes in the maven repository do not come with sources or API. No fun debugging or just hovering ;-)

@Adam: Thx for your always informative blogs!

Posted by Jan Wiemer on April 06, 2012 at 02:37 AM CEST #

@Alexis: thanks, your workaround worked for me beautifully. I am getting a similar error as that posted here but instead related to javax-rpc.

Posted by Brandon on October 13, 2012 at 02:26 AM CEST #

One thing bothers me :)
What does "cut" abbreviation stand for?

Posted by Grzesiek Demecki on April 22, 2014 at 09:20 PM CEST #

@Grzesiek,

bothers you?

Its Class Under Test of course :-)

Cut is also a nice reference to "One Of These Days I will Cut You Into Little Pieces". A nice song to forget the red bar :-)

cheers,

adam

Posted by Adam Bien on April 23, 2014 at 11:05 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
...the last 150 posts
...the last 10 comments
Links
License