Java Champion / JavaOne Rockstar Adam Bien (adam-bien.com) is a self-employed consultant, lecturer, software architect, developer, and author in the enterprise Java sector. He is also the author of several books and articles on Java and Java EE technology, as well as distributed Java programming. adam has posted 59 posts at DZone. View Full User Profile

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

10.20.2010
| 6125 views |
  • submit to reddit

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.

From http://www.adam-bien.com/roller/abien/entry/trouble_with_crippled_java_ee

Published at DZone with permission of its author, adam bien.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Trevor Leach replied on Wed, 2010/10/20 - 9:45am

I'm not sure there is a problem with the packaging. In your case, the javaee-web-api dependency is correctly defined to be in the provided scope, which adds it to the compile-time classpath. You are then free to use whatever implementation you want (geronimo, jboss, etc.) for testing by adding them to the test scope. Their jars won't be put on the compile classpath or added to your WEB-INF/lib. @see http://maven.apache.org/pom.html#Dependencies

Mladen Girazovski replied on Wed, 2010/10/20 - 9:45am

Hi Adam,

Loading a class is needed for mocking,

not really, at least not with JMock2 (my prefered mock library), i think that most, if not all mocking libraries support mocking interfaces, since this was the preffered/original approach of mocking libraries : Mocking interfaces, not real classes

What mock library do you use?

Karl Peterbauer replied on Wed, 2010/10/20 - 11:51am

This was really a genius who came up with class file crippling. It would be interesting to estimate how many developer hours have been wasted because of this ridiculous maneuver. Google shows a lot of hits. It's not only about mocking/testing. Things go wrong sometimes during project setup, so every other solution (throwing runtime exceptions, whatever) is better than blowing up with a totally incomprehensible ClassFormatError. And finally, one might also want to have loadable API classes for - say - annotation-based code generation.

Wojtek Kapusniaczek replied on Wed, 2010/10/20 - 2:38pm

 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

 

 

Grzegorz Grzybek replied on Wed, 2010/10/20 - 11:53pm

Hi Adam

THis particular problem is one of the most important with JavaEE. See my rant at http://www.restfusion.com/blog/2010/02/jee-sdk-loathe-it-or-ignore-it-you-cant-like-it/comment-page-1/#comment-33.

The fact that you cannot find a single, reference JAR+sources for JavaEE artifacts makes me totally mistrustful about entire JavaEE. The fact that there is no single SVN/Git repo for sources (e.g. there is org.glassfish.javax.servlet and a whole bunch of org.apache.geronimo.specs.* jars) causes real trouble in production! Maybe you were lucky, but I've fightied many times with strange errors about missing API methods in "standard" jars on Tomcat, WebSphere and OAS.

That's why my motto is "carry your dependencies in your WEB-INF/lib" - that way you're sure that they'll be used in your unit tests, integration tests, on your CI server and after deployment. Maybe I loose few seconds during deployment, but believe me - in large applications, the deployment time of slightly larger WAR is about 10% of total deployment time - the rest is totally application specific (e.g. loading thousands of rows and creating initial, application-wide data structures).

Once again - I like JavaEE6 - particularly Bean Validation - I've tried it and love it! From the very beginning I can use it safely on my Tomcat6 - I don't need GF3 or JEUS TMAX.

regards

Grzegorz Grzybek

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.