Showing posts with label ant. Show all posts
Showing posts with label ant. 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, 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, 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.


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.

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.

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.