7

I've recently run into a problem with glassfish standalone (v3.1) vs glassfish embedded (v3.1) vs java SE and the way java.endorsed.dirs is used. The specific problem I had is here, but I don't think it's the last time I'm going to run into something similar.

The information I found here and here suggests adding the glassfish endorsed libs to the bootstrap classpath when compiling. However, this bug report suggests it is difficult to get the endorsed libs set correctly when using glassfish embedded.

So, it seems like when I deploy to a standalone glassfish container my application is going to run against the endorsed libs that glassfish includes, but when using the embedded container it won't. I encountered my original problem because the maven-embedded-glassfish-plugin doesn't start glassfish embedded using the endorsed libs like glassfish standalone does. I'm also unsure whether other containers (ex: jboss) include the same set of endorsed libs as glassfish.

So, am I (1)supposed to struggle (a lot) to make sure my application is compiled against the endorsed libs and always deployed to a container that is bootstrapped using the endorsed libs or should I (2)just stick to using what's bundled with Java SE 6?

If I choose (2), will I have to worry about incompatibilities when deploying my application to a container that is bootstrapped with newer endorsed libs?

I would appreciate any insight that anyone can offer.

Community
  • 1
  • 1
Ryan J
  • 2,502
  • 5
  • 31
  • 41

2 Answers2

4

EDIT: The javaee-endorsed-api approach above probably will work fine, but it gives me the willies. I don't think it is produced or maintained anymore. Furthermore the pom.xml it contains within it reflects that at some point it was called javaee-compact-api, and you can see how they strip the implementation classes out of it. By contrast, cherry-picking the API jars you want to use as endorsed (as I recommend below) seems to be more stable and flexible. Finally, if you still want to use the javaee-endorsed-api approach, you can still use the general approach I recommend and point to javaee-endorsed-api.jar instead.

Ryan; I just combed through your long trail on this (touching StackOverflow, the java.net forums, etc.) on the same journey.

During unit or integration testing you'll need to set the java.endorsed.dirs System property, as you know.

The trick is you have to do this in such a way that the JVM running the tests picks it up. And that depends on how you have Surefire running.

If for some reason you have Surefire set to not fork, this is probably a bad thing, and you should re-evaluate your configuration here.

If you have Surefire set to fork, then you might think you could simply include java.endorsed.dirs in a systemPropertyVariables stanza, like this:

<systemPropertyVariables>
  <java.endorsed.dirs>weWillGetToThisInAMoment</java.endorsed.dirs>
</systemPropertyVariables>

...but that would be wrong. The reason is that the program that is actually running is something called the ForkedBooter, and the ForkedBooter programmatically sets system properties for your unit tests. That is, by the time your <systemPropertyVariables> stanza is read by the ForkedBooter it's already too late.

But you can use <argLine> in your Surefire configuration like this:

<configuration>
  <argLine>-Djava.endorsed.dirs=weWillGetToThisInAMoment</argLine>
</configuration>

Now the VM that Surefire forks will have its endorsed directories set appropriately. Now let's talk about what value to supply.

You want to cherry pick the APIs to override. In your case, javax.annotation.* is a legitimate choice. You want to supply the directory in your local Maven repository that houses the relevant jar.

Here is the value that I use:

${settings.localRepository}${file.separator}org${file.separator}glassfish${file.separator}main${file.separator}javaee-api${file-separator}javax.annotation${file.separator}${javaxAnnotationVersion}
  • Maven guarantees that ${settings.localRepository} will expand to the value of where your local Maven repository lives.
  • ${file.separator} is a way of getting the value of System.getProperty("file.separator") in a Maven property replacement.
  • In my case, I've already declared a <dependency> on the GlassFish artifact that bundles up the javax.annotation package as defined in Java EE 6. So here I've constructed a path to the artifact. I've also defined a property named javaxAnnotationVersion, which, for me, is set to 3.1.2.

Once you do all of this, then when Surefire forks a VM to run your unit tests, the endorsed directories will be set to the directory in your local Maven repository containing the jar that houses the javax.annotation classes, and now embedded GlassFish—which runs in-process—will use the Java EE 6 versions of javax.annotation classes instead of the Java SE 6 versions. I hope this helps you out.

Laird Nelson
  • 15,321
  • 19
  • 73
  • 127
2

I may be missing something obvious here but... Isn't GlassFish Embbeded shipped with libraries compatible with the Java EE specs? And aren't those libraries loaded by default? (If they aren't, please fill a bug here: http://java.net/jira/browse/EMBEDDED_GLASSFISH).

What I mean is: You should compile against Java EE spec APIs, and just let the container use it's own implementations.

For the first part, if you use Maven, I like the way Codehaus archetypes sets the endorsed libs. It is both clean and Application Server Agnostic:

<properties>
   <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

...

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
        <compilerArguments>
            <endorseddirs>${endorsed.dir}</endorseddirs>
        </compilerArguments>
    </configuration>
 </plugin>

...

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.1</version>
    <executions>
       <execution>
            <phase>validate</phase>
            <goals>
                <goal>copy</goal>
            </goals>
            <configuration>
                <outputDirectory>${endorsed.dir}</outputDirectory>
                <silent>true</silent>
                <artifactItems>
                    <artifactItem>
                        <groupId>javax</groupId>
                        <artifactId>javaee-endorsed-api</artifactId>
                        <version>6.0</version>
                        <type>jar</type>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

Which is pretty much all you need to compile your projects against Java EE 6 APIs. Any Java EE 6 compliant App Server should provide those services, and you shouldn't be worried about how they are making it available to your application.

The responsibility of bootstrapping Java EE Services should be of your App Server. If you try your own "in house" solution, chances are that JAR Hell will break loose.

Cheers,

Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
  • What you've said is (IMO) the way it _should_ work, but old versions of endorsed libs in Java SE win the 'first match' scenario when being loaded.. I think. Whoever starts the JVM needs to set up java.endorsed.dirs, but it's difficult with some of the embedded containers. For Glassfish Embedded, everything is in one big JAR and I don't think the endorsed libs are guaranteed to be in a specific location. I started an [Arquillian thread](http://community.jboss.org/thread/168521?tstart=0) to see what they think. I added a [Glassfish bug](http://java.net/jira/browse/EMBEDDED_GLASSFISH-131). – Ryan J Jun 27 '11 at 13:02
  • @Ryan J. I just took a look in your bug report, and it is Indeed reproducible. This should be fixed by the GlassFish team following the spirit of my previous post, but until they are able to fix this, heres some quick workarounds: 1. Use a Main Class to start the Server. 2. Switch to other embedded container. 3. Set the endorsed libs using MAVEN_OPTS (uggly, but will get you going immediately...). – Anthony Accioly Jun 27 '11 at 14:04