Search code examples
antclasspathclassloader

Ant System Class Loader does not honor $CLASSPATH, honors $LOCALCLASSPATH


I am implementing an ant task as a wrapper for another class, which loads other several classes using the system class loader. Now, the task is in the same jar of these other classes, so I wonder why it is not finding them, since the task is running

Please notice that my classes are in the $CLASSPATH env variable. The problem will not occur if I export LOCALCLASSPATH=$CLASSPATH

Minimal example:

<taskdef name="mytask" classname="my.package.MyTask"  />

<target name="compile">
    <mytask />
</target>

you can easily see the problem here

public class MyTask extends Task {
   public void execute() throws BuildException {
    try {
        ClassLoader cl = ClassLoader.getSystemClassLoader();

        // this will only print the ant jar file path
        for(URL url: ((URLClassLoader)cl).getURLs()){
        log(url.getFile());
        }

        cl.loadClass("my.package.OtherClass"); // throws an exception

    } catch (Exception ex) {
        throw new BuildException(ex);
    }
  }

}

Solution

  • The ant shell script reworks the classpath internally, so the SystemClassLoader contains only a minimal part of the "real" classpath

    tl;dr: use

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
    

    instead of

        ClassLoader cl = ClassLoader.getSystemClassLoader();
    

    From the Mailing List, Rainer Noack:

    if you're launching ant via shell script, it is using oata.launcher.Launcher.java

    This class reorganises the classpath a bit. The env variable CLASSPATH and the classpath commandline argument are stripped and replaced by the minimum classpath used to launch ant. A child classloader of the system classloader is created with the original CLASSPATH entries. The oata.Project is then loaded with this classloader.

    The problem is that the loader in ClassLoader.getSystemClassLoader() is actually untouched, the one that changes (i.e., the one that honors $CLASSPATH, -lib, etc.) can be retrieved using Thread.currentThread().getContextClassLoader().