Sunday, October 22, 2017

How to deal with Java EE Modules

I recently spent some time trying to figure out why Hammock wasn't working on Java 9.  It was a very weird error, but something people run into quite often.

Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException

Errr ok.  I've been using JAX-B for a long time.  I never realized that it was part of Java EE.  It made sense though, given the javax namespace.  Apparently the fix was to add some configuration when running Maven to include JAX-B's module from the JDK (which I guess I always was using?)

mvn clean install -DargLine="--add-modules java.xml.bind"

The -DargLine passes additional options to the forked surefire JVM, --add-modules (plural!) adds a single module (plus its dependencies) to the classpath, otherwise it's not visible.  All of these are runtime issues, visible when the CDI runtime attempts to start.  So after making this change, I figured things would work.

Caused by: java.lang.ClassNotFoundException: javax.xml.ws.WebServiceFeature

Yay.  Now I remembered why I liked OSGi so much.  Apparently the fix for this one is to add the module java.xml.ws to the build.  So I went ahead and ran

mvn clean install -DargLine="--add-modules java.xml.bind --add-modules java.xml.ws"

Fair enough, another module.  Not too bad.  So after running that I was expecting more modules to be added, and got this output

Caused by: java.lang.ClassNotFoundException: javax.annotation.Priority

Ok, that's a weird one.  Why's it weird?  The @Priority annotation comes from javax.annotation-api JAR.  It's not provided by the system.  CDI runtimes will bring in this JAR (Weld uses the RI, OWB brings in the geronimo spec).  I spent about 10 hours over three days trying to dissect this one.  I tried all sorts of things to enable the javax.annotation module, even though it was on the classpath.  Before I reveal the solution, it's best to understand why the modules I explicitly enabled above are hidden.

These modules come from Java EE, yet the RI for them was in the JDK itself.  By placing the RI in the JDK, we open up external usage for these internal feature set.  We also set a precedent that all Java implementations will include these dependencies.  That can't be done if these modules are expected to be there but aren't.  We lose the build once, run anywhere mantra Java has followed for so long.  The good news though, each of the modules are available are standalone dependencies, distributed via Maven Central.

Anyways, back to how to fix this problem.  It turns out, when you enable the java.xml.ws module, it includes a large number of internal dependencies, including the internal JDK javax.annotation package.  One of the modular changes in the JDK is that a package name can only exist in one module.  Since javax.annotation was coming from the JDK and a JAR on the classpath, the JDK version was being used.  So how to deal with this? Well, it turns out for this use case you cannot rely on JDK provided modules.  By bringing in the actual maven dependencies for these JARs, you can deal with this consistently - not depending on any JDK internals but by publically available JARs.  This is all I had to bring in to make an application compile and run on Java 9:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>javax.xml.ws</groupId>
    <artifactId>jaxws-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>



The great thing, because of the transitive dependencies all needed JARs will work.

Sunday, October 1, 2017

Moving Java EE away from the JCP - an important first step

If you haven't already, sign up for the new EE4J mailing list

I was surprised as others to see this in one of the first few messages on list
It is my understanding that the new specification process will not be using the JCP.
 However, I believe it's an extremely important thing to realize how critical of a step this is.  When Oracle purchased Sun, they brought along everything that was there.  Included in this was the very Sun heavy JCP, which while being open was a pain if you weren't a Sun employee.  Much of this has remained the case with Oracle in the lead.  The JCP is run independently, but by seemingly Oracle staff.

While we've been able to get some JSRs through that are not lead by Oracle, it hasn't been many.  The intention to move EE4J away from the JCP is purely a positive sign that Oracle wants to let the community drive the initiatives.  It allows those who want a say and have a say to drive the direction of this new technology.  If I look at who is on the JCP EC, not all of them are involved in Java EE today.  So why should the future and effectively the vote on whether a Java EE specification is ready to be shipped put forth to a group who may not be interested in Java EE?

To me at least, this is a great sign, an important step, and really a clear indication that we need to look at things differently with EE4J than we ever did with Java EE.  I welcome the future and the innovation that this project is going to create.