Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Thursday, October 05, 2023

Using JAXB in custom Ant tasks on recent Java versions

Apache Ant 1.10.14 was released a few weeks ago https://lists.apache.org/thread/9vhk51nkw9wjxzm7xk2q9xm6s803prmr. Like noted in that announcement, apart from the regular bug fixes, this release also has an important change which allows it to be used in the recently released Java 21 https://inside.java/2023/09/19/the-arrival-of-java-21/. One major goal of Ant project is to make sure that it can be used to build projects using latest versions of Java. As such, the Ant project team keeps a watch on any changes in the Java releases that could affect Ant and does necessary changes in Ant and releases them in its 1.10.x release series.

The announcement of 1.10.14 and this changelog https://github.com/apache/ant/blob/rel/1.10.14/WHATSNEW contains the details of what’s exactly changed in this release, so I won’t go into those details again. In this post I’ll however go through one interesting issue that was brought to my notice by more than one projects, when using recent versions of Java. The issue specifically relates to custom Ant tasks that use JAXB. However, in general, it applies to some of the APIs that have been removed from recent versions of Java (details in https://openjdk.org/jeps/320)

JAXB as noted in the reference doc https://docs.oracle.com/javase/tutorial/jaxb/intro/arch.html is an API and implementation for XML binding. Up until Java 11, JAXB API and implementation classes were shipped as part of the Java runtime. What it meant was that any application code, like custom developed Ant tasks, could just use the JAXB APIs in their code without having to explicitly specific any external library dependency. In Java 11, JAXB along with few other modules was removed from the JDK. The release notes of JDK 11 lists this change https://www.oracle.com/java/technologies/javase/11-relnote-issues.html#JDK-8190378. Additionally, JEP-320 https://openjdk.org/jeps/320 has all the details related to this removal. When using JAXB APIs,  the usage of these modules from the JDK was transparent to the application. So although the application may not explicitly have referred to these module names, it was still reliant on them because they were providing public APIs which the application had references to. Effectively, if a project was using some Ant task which used JAXB, then those projects when they switch to any Java version >= 11 will now start seeing issues due to missing compile and runtime dependency on JAXB. Let’s now consider a simple custom Ant task and see what kind of errors it might encounter. But if you are just interested in the shorter answer and some sample code to fix these classloading issues with JAXB, then please check the end of this article, starting here. For the complete details, please read on.

We will use a trivial custom Ant task implemented by a class called org.myapp.HelloTask, which looks like:


package org.myapp;

import org.apache.tools.ant.Task;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

public class HelloTask extends Task {

    private String message;

    public void setMessage(final String message) {
        this.message = message;
    }

    @Override
    public void execute() {
        try {
            final JAXBContext context = JAXBContext.newInstance(DataContainer.class);
            System.out.println("Created JAXB context " + context);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

        System.out.println(this.message);
    }

    private static class DataContainer {
        private String data;
    }
}

The HelloTask is just here for demonstration and in practice doesn’t provide any real value. This task allows for a message attribute to be specified. Furthermore, in its execute() method it creates a javax.xml.bind.JAXBContext for the application specific DataContainer class and just prints that context. Additionally, it also prints the message that was set when the task is launched. Let’s assume that this task has been compiled and packaged as a jar file and made available to the build process. Now consider this trivial build.xml file which declares this task and then launches it:


<project default="invoke-task">
    <property name="build.dir" value="build"/>
    <property name="jar.name" value="hellotask.jar"/>

    <taskdef name="hello" classname="org.myapp.HelloTask">
        <classpath>
            <pathelement location="${build.dir}/${jar.name}"/>
        </classpath>    
    </taskdef>  

    <target name="invoke-task" description="invokes the HelloTask">
        <hello message="hello world"/>
    </target>       

</project>

There’s not much in this build.xml. All it does is, use a taskdef to define the HelloTask with the classpath containing (just the) jar file containing the HelloTask. Then in the invoke-task target we launch this hello task by passing it a message.

Let’s now run this build against Java 8:

export JAVA_HOME=<path-to-JDK-8>
ant invoke-task

When you run this (on Java 8) you should see the output similar to:


invoke-task:
    [hello] Created JAXB context jar:file:/<path-to-jdk-8>/jre/lib/rt.jar!/com/sun/xml/internal/bind/v2/runtime/JAXBContextImpl.class Build-Id: ...
    [hello] Classes known to this context:
    [hello]   [B
    [hello]   boolean
    [hello]   byte
    [hello]   char
    [hello]   com.sun.xml.internal.bind.api.CompositeStructure
    [hello]   double
    [hello]   float
    [hello]   int
...
    [hello]   long
    [hello]   org.myapp.HelloTask$DataContainer
    [hello]   short
    [hello]   void
    [hello] 
    [hello] hello world

You’ll see that the JAXB usage in the task was successful and the build completed successfully and we didn’t have to configure any classpath to include any JAXB jar files. As noted previously, this is because the Java 8 runtime ships with the relevant JAXB API and implementation classes.

Now, without changing any code in the task or in the build.xml, let’s just switch to a recent Java version. Let’s say Java 17 and run the build:

export JAVA_HOME=<path-to-JDK-17>
ant invoke-task

When you do this, you will now see:

BUILD FAILED
build.xml:5: taskdef A class needed by class org.myapp.HelloTask cannot be found: javax/xml/bind/JAXBException
 using the classloader AntClassLoader[build/hellotask.jar]

You’ll notice that the build now fails and the error message states that the class javax/xml/bind/JAXBException cannot be found in the classpath which as defined in the build.xml only included the hellotask.jar (which just has the org.myapp.HelloTask). As noted previously, this is because the JAXB API and implementation is no longer shipped in the JDK. Applications, like this project, are expected to now include the JAXB API and implementation jars in the application classpath. JEP-320 https://openjdk.org/jeps/320 lists the potential Maven co-ordinates to find such jar(s). In case of JAXB it suggests the JAXB reference implementation available in Maven central repo com.sun.xml.bind:jaxb-ri as a potential candidate. Do note that, just like several other Java EE APIs and implementations, JAXB too has several vendors which implement the JAXB API. A “reference implementation” as the name states is meant to demonstrate the implementation of the specified API. There can, and are, several other vendor implementations for such APIs. It’s upto the applications to choose the ones that they desire to use. In this demonstration, we will use the 2.3.8 version of com.sun.xml.bind:jaxb-ri dependency (available at https://repo.maven.apache.org/maven2/com/sun/xml/bind/jaxb-ri/2.3.8/). There’s no specific reason for my choice of this specific version - it’s only for demo.

Now that we have this dependency made available, let’s include it in the classpath of the taskdef of HelloTask and our build.xml (snippet) will now look like:

...
    <taskdef name="hello" classname="org.myapp.HelloTask">
        <classpath>
            <pathelement location="${build.dir}/${jar.name}"/>
            <!-- JAXB dependencies -->
            <pathelement location="${lib.dir}/jakarta.activation.jar"/>
            <pathelement location="${lib.dir}/jakarta.xml.bind-api.jar"/>
            <pathelement location="${lib.dir}/jaxb-impl.jar"/>
        </classpath>    
    </taskdef>  
...

You’ll notice that the classpath of the taskdef now includes the JAXB related dependency jars. Now let’s rerun the build on Java 17, like previously:

export JAVA_HOME=<path-to-JDK-17>
ant invoke-task

When you run this, you will now see something that starts like this:


BUILD FAILED
build.xml:17: java.lang.RuntimeException: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory]
    at org.myapp.HelloTask.execute(Unknown Source)
    

So the build still fails, but unlike previously where it had failed when trying to define the HelloTask itself, this time it fails in the execute() implementation of the HelloTask.

So why does it fail even with the JAXB jars in the classpath. This has to do with JAXB (and several other Java EE APIs), which rely on thread context classloader. Several of these APIs, including this call to:

final JAXBContext context = JAXBContext.newInstance(DataContainer.class);

relies on thread context classloader to find the JAXB related classes and resources. It expects the thread context classloader, whichever it is, to be able to load these JAXB classes. Thread context classloaders are specific to the thread that is currently executing the code. So let’s quickly see which classloaders are in play in the execute() method of the HelloTask. To see that, let’s add some trivial debug messages in the code, whose snippet will now look like:


    @Override
    public void execute() {
        System.out.println(HelloTask.class + " was loaded by classloader: " + HelloTask.class.getClassLoader());
        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        System.out.println("Context classloader of current thread is " + tccl);
        if (tccl instanceof java.net.URLClassLoader) {
            // let's additionally print the classpath of the URLClassLoader
            final java.net.URL[] classpath = ((java.net.URLClassLoader) tccl).getURLs();
            System.out.println("Context classloader's classpath is " + java.util.Arrays.toString(classpath));
        }
        try {
            final JAXBContext context = JAXBContext.newInstance(DataContainer.class);
            System.out.println("Created JAXB context " + context);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

        System.out.println(this.message);
    }

So what we have done here is that added a few System.out.println messages which prints the classloader which loaded the HelloTask and also prints the current thread’s context classloader. Additionally if the context classloader is a java.net.URLClassLoader, we even print the classpath used by the URLClassLoader. The goal of these debug messages is to see what classloaders are in play when using that JAXB API. Let’s rerun the build again on Java 17 - it’s still expected to fail like previously, but this time we should see these debug messages:

export JAVA_HOME=<path-to-JDK-17>
ant invoke-task

This fails with the same exception stacktrace as previously, but this time you should also see, something like:


[hello] class org.myapp.HelloTask was loaded by classloader: AntClassLoader[build/hellotask.jar:lib/jakarta.activation.jar:lib/jakarta.xml.bind-api.jar:lib/jaxb-impl.jar]
[hello] Context classloader of current thread is java.net.URLClassLoader@6d6f6e28
[hello] Context classloader's classpath is [file:/apache-ant-1.10.14/lib/ant-commons-net.jar, file:/apache-ant-1.10.14/lib/ant-xz.jar, file:/apache-ant-1.10.14/lib/ant-junit4.jar, file:/apache-ant-1.10.14/lib/ant-jai.jar, file:/apache-ant-1.10.14/lib/ant-apache-resolver.jar, file:/apache-ant-1.10.14/lib/ant-jdepend.jar, file:/apache-ant-1.10.14/lib/ant-apache-regexp.jar, file:/apache-ant-1.10.14/lib/ant-apache-log4j.jar, file:/apache-ant-1.10.14/lib/ant-javamail.jar, file:/apache-ant-1.10.14/lib/ant-apache-bcel.jar, file:/apache-ant-1.10.14/lib/ant.jar, file:/apache-ant-1.10.14/lib/ant-netrexx.jar, file:/apache-ant-1.10.14/lib/ant-swing.jar, file:/apache-ant-1.10.14/lib/ant-jsch.jar, file:/apache-ant-1.10.14/lib/ant-junitlauncher.jar, file:/apache-ant-1.10.14/lib/ant-jakartamail.jar, file:/apache-ant-1.10.14/lib/ant-junit.jar, file:/apache-ant-1.10.14/lib/ant-imageio.jar, file:/apache-ant-1.10.14/lib/ant-launcher.jar, file:/apache-ant-1.10.14/lib/ant-antlr.jar, file:/apache-ant-1.10.14/lib/ant-testutil.jar, file:/apache-ant-1.10.14/lib/ant-apache-oro.jar, file:/apache-ant-1.10.14/lib/ant-jmf.jar, file:/apache-ant-1.10.14/lib/ant-apache-xalan2.jar, file:/apache-ant-1.10.14/lib/ant-apache-bsf.jar, file:/apache-ant-1.10.14/lib/ant-commons-logging.jar]

You’ll see that the HelloTask was loaded using an instance of AntClassLoader (which is internal implementation detail of the Ant project) and this classloader has the relevant JAXB jars in its classpath (as seen in the message above). You’ll also notice in the log message that the thread’s context classloader is an instance of URLClassLoader:

[hello] Context classloader of current thread is java.net.URLClassLoader@6d6f6e28

and this instance of URLClassLoader has a classpath which has only Ant specific jars and nothing related to JAXB jars. Now when the specific call to JAXBContext.newInstance(...) gets made it ends up using the URLClassLoader (since it is the thread context classloader) which doesn’t have JAXB jars. Effectively, you end up seeing the classloading failures and the build fails.

So how do we fix this. The important bit here is that the thread context classloader should be the one which has the JAXB classes available, so that it can load them. Java’s java.lang.Thread class allows the context classloader to be changed/switched. In fact, in the Java EE ecosystem, frameworks, servers and other implementations (typically not the application code), switch the thread’s context classloader to a “relevant” classloader at the “right place” and then switch it back to the old context classloader when the operation completes. We will need a similar implementation here in the custom task’s execute() method. Here’s what the snippet will now look like (we no longer need the debug logging, so that’s now been removed):

@Override
    public void execute() {
        // get the current context classloader
        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        try {
            // change the thread context classloader to this task's classloader
            // before using the JAXB API
            Thread.currentThread().setContextClassLoader(HelloTask.class.getClassLoader());
            final JAXBContext context = JAXBContext.newInstance(DataContainer.class);
            System.out.println("Created JAXB context " + context);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        } finally {
            // restore back the old context classloader
            Thread.currentThread().setContextClassLoader(tccl);
        }

        System.out.println(this.message);
    }

Notice that we get hold of the current context classloader and then before calling JAXBContext.newInstance(...) we change the context classloader to the HelloTask’s classloader. The HelloTask’s classloader, as we have seen so far, has the necessary JAXB jars (since we defined it in the classpath of the taskdef in the build.xml) from which it should be able to load the JAXB classes. Finally, and very importantly, in a finally block we restore the context classloader to whatever it was previously - that way rest of the code isn’t impacted by switching the thread context classloader. Let’s now build the project again on Java 17:

export JAVA_HOME=<path-to-JDK-17>
ant invoke-task

When you now run this, you should see:

invoke-task:
    [hello] Created JAXB context jar:file:/lib/jaxb-impl.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class Build-Id: 2.3.8
    [hello] Classes known to this context:
    [hello]   [B
    [hello]   boolean
    [hello]   byte
    [hello]   char
    [hello]   com.sun.xml.bind.api.CompositeStructure
    [hello]   double
    [hello]   float
    [hello]   int
    ...
    [hello]   org.myapp.HelloTask$DataContainer
    [hello]   short
    [hello]   void
    [hello] 
    [hello] hello world

So the build now succeeds.

To summarize, recent versions of JDK have removed certain APIs from the JDK. Some of such APIs are available as external libraries which can be configured in the application classpath. In context of Ant, if a task that is shipped by Ant makes use of such APIs, then the Ant release note and the manual of that task will make a note of such change and will also note what needs to be done to get that task functional. In other cases, where custom tasks are involved and depend on APIs that are no longer part of the JDK, on most occasions they will need to update their build files to include those dependencies in their taskdef’s classpath. In some additional cases, like this very specific JAXBContext API usage, they might even have to do changes to the task’s code to use the right classloader. Do note that I decided to use this specific API of JAXB only to demonstrate the classloader change that would be needed in the task. Not all tasks that use JAXB would need this change in the task’s code - the build.xml classpath changes are expected. Also note that switching of classloaders shouldn’t be done blindly as it can cause other classloading issues. It should only be done when the specific API call in question has semantics which specify the use of a context classloader.

Some of you would be wondering if Ant itself should be doing a change where it sets the “right” thread context classloader before invoking the tasks. It wouldn’t be right for Ant to be doing such a change - depending on what the custom task does, there could be different answers to what is the “right” thread context classloader to use and for what duration. Answers to either of those questions are task specific and as such should be handled/implemented in the (custom) tasks.

Sunday, May 16, 2021

Apache Ant 1.10.10 released - Better test result summary from junitlauncher task

Apache Ant 1.10.10 got released around a month back. Among the usual bug fixes, we added a new enhancement for the "junitlauncher" task.

For those of you who haven't used or know about "junitlauncher" task, it's a new task we introduced a few years back to allow projects using Ant, to be able to use the new JUnit5 testing framework. The previous (and still supported) "junit" task is meant to be used only if you want to continue using just JUnit4. If you plan to use JUnit5 (which also supports JUnit4 style testcases), then you will have to use the "junitlauncher" task.

This "junitlauncher" task has been around for a few years now and some users have reported that its "printSummary" feature isn't of much use. People familiar with the "junit" task will know that when a test gets run, the task prints an instantaneous summary like:

org.myapp.foo.bar.SimpleTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec

This is useful to see a quick summary of the tests being run.

The "junitlauncher" has a "printSummary" attribute which until Ant 1.10.10 version used to print a summary after all the tests had been executed. Furthermore, the printed summary was a summary that the JUnit5 framework generates by default, something like:

[junitlauncher]
[junitlauncher] Test run finished after 5103 ms
[junitlauncher] [         2 containers found      ]
[junitlauncher] [         0 containers skipped    ]
[junitlauncher] [         2 containers started    ]
[junitlauncher] [         0 containers aborted    ]
[junitlauncher] [         2 containers successful ]
[junitlauncher] [         0 containers failed     ]
[junitlauncher] [         1 tests found           ]
[junitlauncher] [         0 tests skipped         ]
[junitlauncher] [         1 tests started         ]
[junitlauncher] [         0 tests aborted         ]
[junitlauncher] [         1 tests successful      ]
[junitlauncher] [         0 tests failed          ]

As you can see, summary of this form isn't really useful. So some of the Ant users requested (https://bz.apache.org/bugzilla/show_bug.cgi?id=64836) this to be improved to provide a summary which resembled what we have with the "junit" task.

This Ant 1.10.10 release now consists that enhancement. When you use "printSummary=true" on the "junitlauncher" task, it will now print a more useful and immediate summary like the "junit" task does:

Running org.myapp.foo.bar.SimpleTest
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec


As usual, the release is available for download at the Ant downloads page https://ant.apache.org/bindownload.cgi. Please give this a try and if you have any suggestion or feedback on this release, please get in touch with us on our mailing lists https://ant.apache.org/mail.html or our issue tracker https://ant.apache.org/bugs.html.

 

Saturday, October 26, 2019

Apache Ivy 2.5.0 released

This week, we released 2.5.0 version of Apache Ivy. Apache Ivy is a build dependency manager that is integrated with the Apache Ant build tool. The download is available as usual at https://ant.apache.org/ivy/download.cgi.

This 2.5.0 version comes after a long gap in releases of Ivy. 2.4.0 was released way back on December 26 2014. After that, the project has seen slowness in development activities. However, with help from the some community members, especially Nicolas Lalevée, back in around 2017, we had some bug fixes and enhancements done. On April 19 2018, we released 2.5.0-rc1. We called it a "rc1" because it was a long time since we had done a project release and wanted users to be aware that there might be some unexpected issues.

2.5.0-rc1 saw some community members using this version and reporting back some bugs that they ran into. We have been able to fix them during this past year and it was now finally time to release it as 2.5.0. We expect the Ivy community to start using this version in favour of older releases and report back any issues that they run into either at https://issues.apache.org/jira/browse/IVY or in our Ivy user mailing list https://ant.apache.org/mail.html

Users migrating from previous versions of Ivy are recommended to use a fresh/clean Ivy local cache to avoid certain issues with cached Ivy metadata that might have been introduced in a previous version (2.5.0-rc1) of Ivy.

The complete list of changes (since 2.4.0) that are part of this release is available at https://ant.apache.org/ivy/history/2.5.0/release-notes.html.

Going forward, the goal is to fix any bugs that are reported and introduce certain enhancements that will help improve the usage of the tool.

Saturday, May 11, 2019

Apache Ant 1.10.6 released - fork mode for junitlauncher and new jmod and link tasks

Apache Ant 1.10.6 has been released this week. This release contains numerous bug fixes as well as some very exciting new features. The complete release notes is available here and the downloads itself are available here. In this article, I will go over some of the new features that have made it into this release.

Running JUnit5 tests in a forked JVM, using junitlauncher task


A while back, Ant 1.10.x introduced support for JUnit5 tests to be launched using the new "junitlauncher" task. Given the nature of changes between JUnit 4.x and JUnit 5, the amount of support introduced in the new "junitlauncher" task was minimal. Based on user feedback about this task, this task has now been enhanced to support "fork" mode. This was one of the most asked for enhancement, in this task. The "fork" mode support in this task now allows users to configure this task to launch the tests in a forked JVM instead of running these tests within the same JVM as the one, the build is currently running in. Fork mode allows much more control over how these tests execute (things like setting up additional JVM arguments just for these tests or even system properties). The complete details of how to use fork mode in this task, is available in the manual for this task. Here's a very basic minimal example of one such usage:

<target name="test-basic-fork">
        <junitlauncher>
         <!-- Imagine test.classpath points to a previously configured path -->
            <classpath refid="test.classpath"/>
            <test name="org.example.myapp.SampleTest" outputdir="${output.dir}">
                <fork dir="${basedir}">
                    <sysproperty key="myapp-system-property" value="hello world!"/>
                </fork>
            </test>
        </junitlauncher>
</target>


The example above, sets up "junitlauncher" task to launch a test class named "org.example.myapp.SampleTest" in a forked JVM. The "fork" element in the example above is configured to setup a Java system property named "myapp-system-property" with a value of "hello world!". When the test executes, this Java system property will be made available to this test or any other class being executed in that forked JVM. More advanced ability of the "fork" element is explained in the manual linked previously.

New jmod and link tasks for Java 9+ tools


Java 9 shipped with a new modular ecosystem. This also brought in new tools to create and manage the Java modules. In this release of Ant 1.10.6, we introduce new tasks - "jmod" and "link", which can be used to create Java modules and then assemble them to create custom JVM runtime images. More details about these tasks can be found in their manuals here and here. A big thanks to Craig Pell who contributed these valuable tasks. More Java 9+ enhancements are being worked upon in Ant and we plan to make them available in future releases.

Please do download this new version of Ant and provide us feedback, suggestions in our user mailing list.

Monday, August 27, 2018

Java 11 release candidate now available - time to try it out

The Java language development team has just released a release candidate build for Java 11, last week.

Java 11 release brings in good number of new features as noted at http://openjdk.java.net/projects/jdk/11/#Features. Personally, the TLS 1.3 and the HTTP client API are of special interest to me. Given the nature of changes in this version, it's important to try out this release candidate to make sure that these changes don't cause regressions or introduce change in semantics or features that your application or framework relies on. So this is a good time to try out this build and provide your feedback to the team.

The release can be downloaded from http://jdk.java.net/11/. Any issues, feedback and suggestions can be provided by following the process noted in the "Feedback" section on that downloads page.

Monday, July 16, 2018

Apache Ant 1.9.13 and 1.10.5 released - Supports Java 11 single-file source programs

We just released 1.9.13 and 1.10.5 versions of Apache Ant. As usual, you can download it from the Ant project download page.

Both these versions are mainly bug fix releases. The 1.10.5 version however has a new enhancement to the "java" task. As I blogged previously - Java 11 introduces a new feature where you can execute single-file Java programs without having to explicitly compile them first. Ant 1.10.5 release now supports this feature through a new "sourcefile" attribute in the "java" task. More about it can be found the manual of that task.

A simple usage example of this new feature of the "java" task is as follows:

<project default="launch-java" name="Java 11 - launch single-file source program">

 <target name="launch-java"
            description="Simple example of single-file source program execution,
             introduced in Java 11">

        <!-- Make sure Java 11 version is being used -->
        <condition property="java11">
            <javaversion atleast="11"/>
        </condition>    
        <fail unless="java11">Java 11 runtime version is necessary to run this example</fail>        

        <mkdir dir="${basedir}/javasource"/>
        <!-- Write out simple Java code into a file -->
        <echo file="${basedir}/javasource/HelloWorld.java">
            import java.nio.file.Files;
            import java.nio.file.Paths;
            import java.io.BufferedWriter;
            public class HelloWorld {
                public static void main(String[] args) throws Exception {
                    System.out.println("Hello world, " + args[0] + "!");
                }
            }
        </echo>
        <!-- launch the Java source file, using the "sourcefile" attribute -->
        <java sourcefile="${basedir}/javasource/HelloWorld.java" fork="true" failonerror="true" logerror="true">
            <arg value="Java 11"/>
        </java>
    </target>
</project>

As you'll notice, the build file uses the "java" task to set the "sourcefile" attribute to point to a Java source file. The rest of the usage details of the "java" task, including passing arguments to the program, continue to remain the same as before.

When you run "ant" on this build file, you should see the following output:

[java] Hello world, Java 11!

Of course, you will need to use a Java 11 binary to run this against. You can get the early accessible Java 11 binary from here.


Friday, July 13, 2018

Java 11 upcoming features - Launch Single-File source programs

Java 11 is nearing completion and it's entered the rampdown phase. It almost feels like a few weeks back that Java 9 was released and here we are, within a few months of Java 11 being released. Given the new release process and timelines for Java, this will become a common thing. Whether that's a good thing or not, we'll keep it aside.

The changes coming in Java 11 are listed here. These are some nice enhancements and features coming in this release. Two of them that I'm really excited about are:

    - HTTP client (standard) http://openjdk.java.net/jeps/321 which will bring in HTTP client APIs as part of the Java language.
    - Launch Single-File Source-Code Programs http://openjdk.java.net/jeps/330

In this article, I will go through the "Launch Single-File Source-Code Programs" feature. What this enhancement proposes to accomplish is to make it easy for running Java code which consists of a single file with the "main()" method in it.

Imagine you have a simple HelloWorld program as follows in a file HelloWorld.java under org/myapp directory:


package org.myapp;


public class HelloWorld {
    
    public static void main(String[] args) throws Exception {
        System.out.println("Hello World!");
    }
}


Right now, without the proposed feature, in order to run this program, the user has to first compile it using javac command:


javac org/myapp/HelloWorld.java

Once that succesfully compiles, you then run the java command to execute the program:


java org.myapp.HelloWorld


So it's a 2 step process. It looks trivial even for beginners, but it can still be made simpler not just for beginners but even developers who regularly work with Java.

Once Java 11 gets released (or if you want to try it now, you can get the early access builds from http://jdk.java.net/11/) we can run the above program as follows (as a single command):


java org/myapp/HelloWorld.java


Notice the difference here:
     1. one, we no longer use the javac command to explicitly compile the source
     2. The java command is now passed the path to the source file (org/myapp/HelloWorld.java) instead previously where we used to pass it the fully-qualified classname.

This difference is minor but important, since the java command now "understands" that it now has to internally do whatever it's necessary (like compiling the source) when it's passed a file path whose file name ends with the .java extension. Of course, such a file is expected to contain regular/valid Java code with a top level class exposing the "public static void main(String[])" method.

Furthermore, just like your regular Java programs you can continue to pass application specific arguments to the program as before. For example, for a calculator program which looks below, in a org/myapp/Calculator.java file:


package org.myapp;


public class Calculator {
 
 public static void main(final String[] args) throws Exception {
  final int sum = Integer.parseInt(args[0]) +  Integer.parseInt(args[1]);
  System.out.println(args[0] + " + " + args[1] + " = " + sum);
 }
}

you can pass the program arguments as follows:


java org/myapp/Calculator.java 2 4

where 2 and 4 are passed as the program arguments and you would see the output as follows:

2 + 4 = 6
This feature also adds support for "shebang" files, files which are expected to hava valid Java code plus a "shebang". Personally, I'm not too fond of this specific aspect of the feature. However, the good thing is, the JDK team took feedback from the community and made this additional aspect of a feature, non-intrusive (for tools/commands which already deal with Java source files) and something that some of us can ignore if we don't want to use it. The details of when/how to use the "shebang" files for this feature are explained in the linked JEP-330.

So far, although Java 11 hasn't been released, I have been using the early access builds and extensively using this feature for some of my regular work which sometimes involves coming up with short programs that help reproduce an issue. I usually don't use IDEs for things like these, so it's been a welcome enhancement to be able to issue a single command against such files and have them executed.



Monday, May 14, 2018

Apache Ivy 2.5.0-rc1 released - Now allows timeouts on resolvers

A few weeks back, we released the 2.5.0-rc1 version of Apache Ivy. Apache Ivy is a dependency management build tool, which usually is a used in combination with Apache Ant. The download is available on the project download page

This release is significant since the last release of Apache Ivy was way back in December 2014. So it's more than 3 years since the last official years. During these past few years, the project development stalled for a while. I use Apache Ivy in some of our projects and have been pretty happy with the tool. It's never a good sign to see one of your heavily used tools to be no longer under development or even have bug fixes. So a year or so back, I decided to contribute some bug fixes to the project. Over time, the project management committee invited me to be part of the team.

We decided that the first obvious, immediate goal would be to revive the project and do a formal release with bug fixes. This 2.5.0-rc1 is the result of that effort which started almost a year back. A lot of changes have gone into this release and also a good number of enhancements have made it into this release. This release has been a result of contributions from various different members from the community. The complete list of release notes is available here

We intentionally named this release 2.5.0-rc1 (release candidate) since it's been a while we have done an official release and also given the nature of changes. Please give this release a try and let us know how it goes. Depending on the feedback, we will either release 2.5.0 or 2.5.0-rc2. As usual, some of us from the development team keep an active watch in the ivy user mailing list. So if you have any feedback or questions, please do drop a mail to us, there.

Now coming to one of the enhancements in this release - there's been more than one. One of the issues I personally had was if the repository, backing a dependency resolver configured for Ivy, had some connectivity issues, the build would just hang. This was due to the inability to specify proper timeouts for communicating with these repositories through the resolver. As of this release, Ivy now allows you to configure timeouts for resolvers. This is done through the use of (the new) timeout-constraints element in your Ivy settings file. More details about it are here. Imagine you have a url resolver which points to some URL. The URL resolver would typically look something like:

<url name="foo">
  <ivy pattern=.../>
  <artifact pattern=.../>
  <artifact pattern=.../>
</url>



Let's now try and configure a connection timeout for this resolver. The first thing you would do is define a named timeout-constraint, like below:

<timeout-constraints>
        <timeout-constraint name="timeout-1" connectionTimeout="60000" />
</timeout-constraints>


The value for the name attribute can be anything of your choice. The value for connectionTimeout attribute is represented as a timeout in milli seconds. In the above example, we configure the "timeout-1" timeout-constraint to be of 1 minute. You can even specify a readTimeout which too is in milli seconds. More about this element can be found in the documentation.

As you might notice, we have just defined a timeout-constraint here but haven't yet instructed Ivy to use this constraint for some resolver. We do that in the next step, where we set the "timeoutConstraint" attribute on the URL resolver that we had seen before:


<url name="foo" timeoutConstraint="timeout-1">
  <ivy pattern=.../>
  <artifact pattern=.../>
  <artifact pattern=.../>
</url>


Notice that the value of "timeoutConstraint" attribute now points to "timeout-1" which we defined to have a 1 minute connection timeout. With this, when this URL resolver gets chosen by Ivy for dependency resolution, this connection timeout will be enforced and if the connections fails to be established within this timeout, then an exception gets thrown instead of the build hanging forever.

Although the example uses a URL resolver to setup the timeout constraint, this feature is available for all resolvers that are shipped out of the box by Ivy. So you can even use it with the ibiblio resolver (which communicates with Maven central) too.


Like I noted earlier, please do give this release a try and let us know how it goes.

Friday, May 11, 2018

VMWare vijava - The curious case of "incorrect user name or password" exception

In one of the projects I have been involved in, we use yavijava (which is a fork of vijava) library to interact with vCenter which hosts our VMs. vCenter exposes various APIs through their webservice endpoints which are invoked through HTTP(s). The yavijava library has necessary hooks which allows developers to use a HTTP client library of their choice on the client side to handle invocations to the vCenter.

In our integration, we plugged in the Apache HTTP client library, so that the yavijava invocations internally end up using this HTTP library for interaction. Things mostly worked fine and we were able to invoke the vCenter APIs. I say mostly, because every once in a while we kept seeing exceptions like:

InvalidLogin : Cannot complete login due to an incorrect user name or password.

This was puzzling since we were absolutely sure that the user name and password we use to interact with the vCenter was correct. Especially since all of the previous calls were going through fine, before we started seeing these exceptions.

The exception stacktrace didn't include anything more useful and neither did any other logs. So the only option that I was left with was to go look into the vCenter (server side) event logs to see if I can find something. Luckily, I had access to a setup which had a vSphere client, which I then used to connect to the vCenter. The vSphere client allows you to view the event logs that were generated on the vCenter.

Taking a look at the logs, showed something interesting and useful. Every time, we had run into this "incorrect user name or password" exception on the client side, there was a corresponding event log on the vCenter server side at INFO level which stated "user cannot logon since user is already logged on". That event log was a good enough hint to give an idea of what might be happening.

Based on that hint, the theory I could form was, somehow for an incoming (login) request, vCenter server side notices something on the request which gives it an impression that the user is already logged in. Given my background with Java EE technologies, the immediate obvious thing that came to mind was that the request was being attached with a "Cookie" which the server side uses to associate requests against a particular session. Since I had access to the client side code which was issuing this login request, I was absolutely sure that the request did not have any explicitly set Cookie header. So that raised the question, who/where the cookie was being associated with the request. The only place that can happen, if it's not part of the request we issued, is within the HTTP client library. Reading up the documentation of Apache HTTP client library confirmed the theory that the HTTP client was automagically associating a (previously generated) Cookie against the request.

More specifically, the HTTP client library uses pooled connections. When a request is made, one of the pooled connections (if any) gets used. What was happening in this particular case  was that, a previous login would pick up connection C1 and the login would succeed. The response returned from vCenter for that login request would include a Cookie set in the response header. The Apache HTTP client library was then keeping track of this Cookie against the connection that was used. Now when a subsequent login request arrived, if the same pooled connection C1 gets used for this request, then the HTTP client library was attaching the Cookie that it kept track against this connection C1, to this new request. As a result, vCenter server side ends up seeing that the incoming login request has a Cookie associated with it, which says that there's already a logged in session for that request. Hence, that INFO message in the event logs of vCenter. Of course, the error returned isn't that informative and in fact a bit misleading since it says the username/password is incorrect.

Now that we know what's going on, the solution was pretty straightforward. Apache HTTP client library allows you to configure Cookie policy management. Since in our case, we wanted to handle setting the Cookie explicitly on the request, we decided to go with the "ignoreCookies" policy which can be configured on the HTTP client. More about this can be found in the HTTP client library documentation (see the "Manual Handling of Cookies" section). Once we did this change, we no longer saw this exception anymore.


There isn't much information about this issue anywhere that I could find. The closest I could find was this forum thread https://sourceforge.net/p/vijava/discussion/826592/thread/91550e2a/. It didn't have a conclusive solution, but it does appear that it's the same issue that the user there was running into (almost 7 years back!)

Wednesday, March 28, 2018

Ant 1.10.3 released with JUnit 5 support

We just released 1.9.11 and 1.10.3 versions of Ant today. The downloads are available on the Ant project's download page. Both these releases are mainly bug fix releases, especially the 1.9.11 version. The 1.10.3 release is an important one for a couple of reasons. The previous 1.10.2 release, unintentionally introduced a bunch of changes which caused regressions in various places in Ant tasks. These have now been reverted or fixed in this new 1.10.3 version.

In addition to these fixes, this 1.10.3 version of Ant introduces a new junitlauncher task. A while back, the JUnit team has released JUnit 5.x version. This version is a major change from previous JUnit 3.x & 4.x versions, both in terms of how tests are written and how they are executed. JUnit 5 introduces a separation between test launching and test identification and execution. What that means is, for build tools like Ant, there's now a clear API exposed by JUnit 5 which is solely meant to deal with how tests are launched. Imagine something along the lines of "launch test execution for classes within this directory". Although Ant's junit task already supported such construct, the way we used to launch those tests was very specific to Ant's own implementation and was getting more and more complex. With the introduction of this new API within the JUnit 5 library, it's much more easier and consistent now to launch these tests.

JUnit 5, further introduces the concept of test engines. Test engines are responsible for "identifying" which classes are actually tests and what semantics to apply to those tests. JUnit 5 by default comes with a "vintage" engine which identifies and runs JUnit 4.x style tests and a "jupiter" engine which identifies and runs JUnit 5.x API based tests.

The "junitlauncher" task in Ant introduces a way to let the build specify which classes to choose for test launching. The goal of this task is to just launch the test execution and let the JUnit 5 framework identify and run the tests. The current implementation shipped in Ant 1.10.3, is the basic minimal for this task. We plan to add more features as we go along and as we get feedback on it. Especially, this new task doesn't currently support executing these tasks in a separate forked JVM, but we do plan to add that in a subsequent release.

The junit task which has been shipped in Ant since long time back, will continue to exist and can be used for executing JUnit 3.x or JUnit 4.x tests. However, for JUnit 5 support, the junitlauncher task is what will be supported in Ant.

More details about this new task can be found in the junitlauncher's task manual. Please give it a try and report any bugs or feedback to our user mailing list.

Tuesday, February 20, 2018

WildFly 12.0.0.Beta1 tagged and available

WildFly 12.0.0.Beta1 has been tagged and has been (I think) officially released. The announcement happened a few days back in the dev mailing list and unlike the previous releases, this time the release binaries seem to be only available in Maven repository and can be obtained from here - WildFly 12.0.0.Beta1 distribution (the .tar.gz and .zip are the relevant ones). The list of changes for this release can be found in the JIRA release notes.

As you'll notice in the release notes, there's some initial support for EE 8 specs, including Servlet 4.0 among others. Plus there's also numerous bug fixes in this release from the previous 11.0.0.Final version which was released some months back. As usual, please give this version a try and if there are any issues or feedback that you would like to report, please start a discussion in the WildFly user forum

If you haven't been following the WildFly dev mailing list, there's also a discussion which outlines the release plans for WildFly going forward. You can find that discussion here

Finally, there's been major changes to Java EE processes and committee and even the name, over the past year. If you haven't been following those changes, then you can read through Mark Little's recent blogs including the most recent ones which talk about the new brand name for Java EE  and setting up of the working group.

Wednesday, February 07, 2018

Apache Ant new versions (1.9.10 and 1.10.2) released

This past year has been pretty hectic, so I haven't had a chance to update this blog more often.

In my limited spare time last year, I started contributing to Apache Ant project. Although Ant probably isn't as widely used as some years back, it still is used in many projects, as the build tool. Some of the products I'm involved in, does use Ant and that motivated me to contribute to some bug fixes in Ant. After a period of time, last year, I was invited to be a committer and a few weeks back, to be part of the Ant project management committee (PMC), which I consider a honour.

Just today, we released a couple of new versions of Ant - 1.9.10 and 1.10.2. These are essentially bug fix releases but do contain some new enhancements. The complete release notes, for each of these releases, can be found  here and here.

The downloads are available from the project's download page and the full announcement, in the mailing list, can be read here

If you have any issues/suggestions/feedback about the project, feel free to report it in the user mailing list which is listed on this page.

Friday, February 17, 2012

JBoss AS 7.1.0.Final "Thunder" released - Java EE 6 Full Profile certified!


After just about more than a year of development on JBoss AS7, we have now released 7.1.0.Final "Thunder"! The download is available at the usual place here. This is a really big achievement for the JBoss AS7 team and we are really proud about this release.

This release contains numerous bug fixes from 7.1.0.CR1b which was released a few months back. But the biggest news about this release is that JBoss AS 7.1.0.Final is Java EE 6 Full Profile certified! I'm sure a lot of our users will be very happy about this news. AS 7.0.x was Web Profile certified but I have seen in the forums that many of you were waiting for the Full Profile certification to happen. So here's a very good reason to start using JBoss AS7, if you haven't done yet.

Apart from the Full Profile certification, AS 7.1.0.Final contains a lot of bug fixes and other JIRA issues resolved. The entire release notes can be found here.

Like in some of my previous posts on AS7 releases, in this post I'll explain atleast one new feature of this release. Many of you would know that JBoss AS7 is very different compared to the previous JBoss AS versions, on various counts. One prominent difference is that we no longer have numerous XML files in the distribution, configuring various services. Instead we just have *one* single configuration file which governs the entire server. Furthermore, unlike previous JBoss AS versions, JBoss AS7 (prior to 7.1.0.Final) did not allow *deploying* XML files to configure datasources and JMS queues. However, the community members have been repeatedly asking for this feature and JBoss AS 7.1.0.Final now allows deploying of datasources and JMS queues via application specific XML files (in addition to configuring them centrally in the domain/standalone configuration file). So let's take a quick look at how it's done in 7.1.0.Final.

Deploying datasource via -ds.xml files in JBoss AS 7.1.0.Final

The datasource file is expected to end with the -ds.xml suffix, like in previous JBoss AS releases. You can place the *-ds.xml file in the JBOSS_HOME/standalone/deployments folder or even package it in the application under the META-INF folder of the application. If it's a .war application, then the *-ds.xml is expected to be right under the WEB-INF folder of the .war.

The *-ds.xml is expected to follow the jboss-as-datasources xsd which looks like this. So you have a datasources element under which you can define multiple datasource elements. In this example, we'll try and create a MySQL datasource and deploy it as  mysql-ds.xml.

Before creating the datasource, we first have to install the database driver. AS7 allows you install the database driver either as a deployment or as JBoss Module. For more details on this, see this article. In this post, we'll deploy the driver as a JBoss Module.

Create and install the database driver

As a first step, we'll require the MySQL driver jar file. I downloaded the driver jar from the MySQL download site here. The step to create the JBoss Module for this driver involves creating a module.xml which looks like this and is named module.xml:

 <module xmlns="urn:jboss:module:1.1" name="mysql">  
   
   <resources>  
     <resource-root path="mysql-connector-java-5.1.18-bin.jar"/>  
   </resources>  
   <dependencies>  
     <module name="javax.api"/>  
     <module name="javax.transaction.api"/>  
   </dependencies>  
 </module>  
We place the mysql-connector-java-5.1.18-bin.jar and this module.xml file in JBOSS_HOME/modules/mysql/main folder (you'll have to create the mysql/main folder). That completes the JBoss Module creation for the MySQL driver. Now let's install this driver so that it gets registered in the standalone/domain configurations. In this example, we'll be using the standalone server. So let's start the server using:
 ./standalone.sh  
Once the server is up, let's open the Command Line Interface (CLI) utility which is shipped in AS7. The CLI startup script is in the JBOSS_HOME/bin folder and can be started as follows (more details about the CLI can be found here)
 ./jboss-cli.sh --connect  
Once connected successfully, we'll add the jdbc-driver using the following command:
 /subsystem=datasources/jdbc-driver=mysql-5-driver:add(driver-name=mysql-5-driver, driver-class-name=com.mysql.jdbc.Driver, driver-module-name=mysql)  
So here we are naming the driver as "mysql-5-driver" (you can name it anything). The driver-module-name points to the "mysql" JBoss Module that we created in previous step. The driver-class-name is the fully qualified classname of the MySQL driver. In this case, it's com.mysql.jdbc.Driver.

A successful execution of that command will show the output as success:
 [standalone@localhost:9999 /] /subsystem=datasources/jdbc-driver=mysql-5-driver:add(driver-name=mysql-5-driver, driver-class-name=com.mysql.jdbc.Driver, driver-module-name=mysql)  
 {"outcome" => "success"}  
   
The installation will be persisted in the configuration file which was used to start the server. In this case it's the standalone.xml and this is how it looks like after the driver has been installed:
 <subsystem xmlns="urn:jboss:domain:datasources:1.0">  
      ...  
           <drivers>  
                ...  
                <driver name="mysql-5-driver" module="mysql">  
                     <driver-class>com.mysql.jdbc.Driver</driver-class>  
                </driver>  
           </drivers>  
      </datasources>  
 </subsystem>  
We are now done with the driver installation. Now let's move on and create the mysql-ds.xml file.

Create the mysql-ds.xml file

As previously mentioned, the mysql-ds.xml should follow the jboss-as-datasources xsd. Here's how the file looks like in our case:
 <?xml version="1.0" encoding="UTF-8"?>  
 <datasources>  
   <datasource jndi-name="java:jboss/datasources/MySQLDS" enabled="true" use-java-context="true"  
         pool-name="MySQLDS">  
     <connection-url>jdbc:mysql://localhost:3306/test</connection-url>  
     <driver>mysql-5-driver</driver>  
     <security>  
       <user-name>foo</user-name>  
       <password>bar</password>  
     </security>  
   </datasource>  
 </datasources>  
Let's see what that xml file contains. The "jndi-name" is the name to which the datasource will be bound to (you can use a name of your choice. Ideally, it would be good to bind them in java:jboss/datasources/ namespace). The "enabled=true" indicates that the datasource should be enabled after being deployed. The "use-java-context" attribute is used to indicate that the JNDI name should be bound under the java: namespace. The "connection-url" is the URL to be used for connecting to the MySQL database (check MySQL documentation for more details about the connection-url). The "driver" element points to the installed JDBC driver that we created in the previous step. In our example, we named it mysql-5-driver and that's what we use here. Finally, the "security" section contains the username and password information for connecting to the database. Make sure you use the appropriate values for all these configurations.

So now let's place this mysql-ds.xml in the JBOSS_HOME/standalone/deployments folder and see JBoss AS7 hot deploy it (if the server is already running). The logs will show the following on successful deployment:
 14:05:55,829 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) JBAS015876: Starting deployment of "mysql-ds.xml"  
 14:05:55,847 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) JBAS010400: Bound data source [jboss/datasources/MySQLDS]  
   
So that's it! We have successfully deployed the MySQL datasource through a -ds.xml file in 7.1.0.Final!

So download this new version and start deploying your applications and start using these features. We'll be blogging more about the features in this release, in the upcoming days (after the AS7 developers get some much needed sleep :) ). So keep an eye on the jboss.org blogs. If you run into any issues with AS7, feel free to visit our user forum and ask for help.

Wednesday, February 09, 2011

@Resource and the new lookup attribute - How to avoid compilation and runtime problems

More and more users have started moving to Java EE6 functionality and one of the common question that keeps coming up in the forums these days is about the use of the "lookup" attribute of the @Resource annotation. Java EE6 introduced this new attribute to the @javax.annotation.Resource annotation (javadoc). In the previous version, this attribute wasn't available. So now users can start using this attribute as follows:

package org.myapp;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

/**
 * Author: Jaikiran Pai
 */
@Stateless
public class SimpleSLSB
{

   @Resource(lookup = "test")
   private DataSource dataSource;


   public void doNothing()
   {
      // nothing!
   }
}


Simple enough! However, the problem arises when you try to compile this code or even run it. Compiling this code with JDK 1.6 leads to this compile time error:

[ERROR] /NotBackedUp/jpai/business/me/resource-lookup/src/main/java/org/myapp/SimpleSLSB.java:[36,13] cannot find symbol
[ERROR] symbol  : method lookup()
[ERROR] location: @interface javax.annotation.Resource


The root cause of this issue is that the standalone JDK itself ships its own version of @javax.annotation.Resource which doesn't have this lookup attribute (javadoc). The compiler ends up using the version shipped in the JDK.

So how do we get past this problem. The solution is simple - all you have to do is set the -Djava.endorsed.dirs system property to point to the folder containing the jar which has the new @javax.annotation.Resource annotation. The -Djava.endorsed.dirs needs to be set while compiling the program as well as while running the program. Depending on what tool you use to compile the program, there are different ways to set this system property. Let's see how we handle this in a Maven project.

Building through Maven:

Maven is a build tool which is used in many project these days. Maven uses a build file named pom.xml which can be used to setup the dependencies of the project as well as other build related configurations. I'll assume that those of you who are using Maven, already know all these details and just want to see how to get the @Resource(lookup="") working within a Maven project. So here it is:

1) Use the maven-dependency-plugin to "copy" the jar containing the new javax.annotation.* classes into some folder within your project. In this example, I'm copying it over to a folder named "endorsed" within the project's build directory:

   <build>
      <plugins>

         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.1</version>
            <executions>
               <execution>
                  <goals>
                     <goal>copy</goal>
                  </goals>
                  <configuration>
                     <!-- Configure the plugin to copy the jar containing javax.annotation.*
                        to a folder named "endorsed" within the project's build directory -->
                     <artifactItems>
                        <artifactItem>
                           <groupId>org.jboss.spec.javax.annotation</groupId>
                           <artifactId>jboss-annotations-api_1.1_spec</artifactId>
                        </artifactItem>
                     </artifactItems>
                     <outputDirectory>${project.build.directory}/endorsed</outputDirectory>
                  </configuration>
               </execution>
            </executions>
         </plugin>

       ...



2) Now let's instruct the compiler plugin and the surefire plugin (which runs your unit tests) to use the jar from the project's endorsed folder. As I said eariler, we just need to set the -Djava.endorsed.dirs property to make this happen. So here's how we do it for these 2 plugins:

...
<!-- Setup the compiler plugin to use the endorsed directory containing
          our javax.annotation.* classes -->
         <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>
               <!-- Setup the compiler plugin to use the endorsed directory containing
               the jar for javax.annotation.* classes. Remember that we setup this folder
               via the maven-dependency-plugin configuration, above. -->
               <compilerArgument>-Djava.endorsed.dirs=${project.build.directory}/endorsed</compilerArgument>
            </configuration>
         </plugin>

         <!-- Setup surefire plugin to use the endoresed directory containing our javax.annotation.* classes -->
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
               <!-- Setup the surefire plugin to use the endorsed directory containing
               the jar for javax.annotation.* classes. Remember that we setup this folder
               via the maven-dependency-plugin configuration, above. -->
               <argLine>-Djava.endorsed.dirs=${project.build.directory}/endorsed</argLine>
            </configuration>
         </plugin>

      </plugins>
   </build>


That's it! We now have setup our project to use the correct endorsed jars. Here's the complete pom.xml for this sample project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jaikiran</groupId>
    <artifactId>resource-lookup</artifactId>
    <version>1.0</version>

   <build>
      <plugins>

         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.1</version>
            <executions>
               <execution>
                  <goals>
                     <goal>copy</goal>
                  </goals>
                  <configuration>
                     <!-- Configure the plugin to copy the jar containing javax.annotation.*
                        to a folder named "endorsed" within the project's build directory -->
                     <artifactItems>
                        <artifactItem>
                           <groupId>org.jboss.spec.javax.annotation</groupId>
                           <artifactId>jboss-annotations-api_1.1_spec</artifactId>
                        </artifactItem>
                     </artifactItems>
                     <outputDirectory>${project.build.directory}/endorsed</outputDirectory>
                  </configuration>
               </execution>
            </executions>
         </plugin>

         <!-- Setup the compiler plugin to use the endorsed directory containing
          our javax.annotation.* classes -->
         <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>
               <!-- Setup the compiler plugin to use the endorsed directory containing
               the jar for javax.annotation.* classes. Remember that we setup this folder
               via the maven-dependency-plugin configuration, above. -->
               <compilerArgument>-Djava.endorsed.dirs=${project.build.directory}/endorsed</compilerArgument>
            </configuration>
         </plugin>

         <!-- Setup surefire plugin to use the endoresed directory containing our javax.annotation.* classes -->
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
               <!-- Setup the surefire plugin to use the endorsed directory containing
               the jar for javax.annotation.* classes. Remember that we setup this folder
               via the maven-dependency-plugin configuration, above. -->
               <argLine>-Djava.endorsed.dirs=${project.build.directory}/endorsed</argLine>
            </configuration>
         </plugin>

      </plugins>
   </build>

    <dependencies>
       <!-- EJB API dependency -->
       <dependency>
          <groupId>org.jboss.spec.javax.ejb</groupId>
          <artifactId>jboss-ejb-api_3.1_spec</artifactId>
          <version>1.0.0.Final</version>
       </dependency>

       <!-- javax.annotation.* dependency -->
       <dependency>
          <groupId>org.jboss.spec.javax.annotation</groupId>
          <artifactId>jboss-annotations-api_1.1_spec</artifactId>
          <version>1.0.0.Final</version>
       </dependency>
    </dependencies>
</project>


Running "mvn clean install" will now show a build success:

[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ resource-lookup ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /NotBackedUp/jpai/business/me/resource-lookup/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ resource-lookup ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /NotBackedUp/jpai/business/me/resource-lookup/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ resource-lookup ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.5:test (default-test) @ resource-lookup ---
[INFO] No tests to run.
[INFO] 
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.087s
[INFO] Finished at: Wed Feb 09 13:02:02 IST 2011
[INFO] Final Memory: 9M/129M
[INFO] ------------------------------------------------------------------------


Although it did not involve much to set this up in Maven, it did however require setting it up with the use of 3 separate plugins. It would have been far more easier if there was a way in Maven where we could just add a "dependency" to the endorsed dir and let all the relevant plugins use it without the developer having to configure each of those separately. But anyway, we got what we were after - a successful compilation of that class using @Resource(lookup="").