How to get classpath from classloader?

I am using some third party code which when given a '-classpath' command line argument doesnt set the java.class.path, but instead just creates a classloader, adds all the urls for the items on the command line specified classpath to the classloader, and then sets it to be the context classloader. In a plugin class to this code that I have written, I get an instance of this classloader, and somehow need to use it to get back the underlying classpath, so that I can use it in an invocation of JavaCompiler.getTask(...) and compile some other code on the fly. However there doesn't seem to be anyway to get the ClassPath from the ClassLoader, and as java.class.path is unset, I can't seem to access the underlying classpath that the application was initially invoked with...Any ideas?

Answers


If the classloader uses URLs, it must be a URLClassloader. What you have access to is the URLs which defines the classpath for it along side with its parent ClassLoader.

To get the URLs, simply do the following:

((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()

UPDATE: My original answer below is woefully inadequate, now that I have spent three years developing FastClasspathScanner, and have had a large number of bug reports filed about certain classpath environments not working with this library. FastClasspathScanner now handles numerous complex classpath specification mechanisms. Even just finding the classpath can be insanely complicated in the general case (let alone scanning it), since there are so many ways to add jars and directories to the classpath.

For one thing, the code I gave below only handles URLClassLoader, and a lot of major runtime environments and containers don't extend this, they implement their own classloader from scratch. But it gets much more complicated than this in the case of Java 9+, since even though the traditional classpath still exists, everything in the future will be moving towards using the module path, not the classpath. Modules have URLs, but they are "jrt:/" URLs, not "file:/" URLs, and module URLs don't actually include a file path, only the module name -- so you can't even in general locate the module on disk. Your only option is to use the (heavily encapsulated) module system to work with modules. I wrote about how to scan the module path here.

FastClasspathScanner handles numerous complex classpath specification mechanisms, so you don't need to reinvent the wheel. You can fetch a list of classpath entries from FastClasspathScanner -- this will save you the trouble of trying to get something working with all the diverse classpath specification mechanisms you find out in the wild. (Apologies if that last link breaks -- the API and docs for FCS will be changing soon.)

--

[Old answer -- obsolete:]

The other answers are correct in most situations, but it gets more complicated than that in some settings: e.g. Maven, Tomcat and JUnit have their own classpath support and they don't use the system classpath.

So far this is the most complete system I have managed to come up with (this code is from my Fast Classpath Scanner project):

/** The unique elements of the classpath, as an ordered list. */
private final ArrayList<File> classpathElements = new ArrayList<>();

/** The unique elements of the classpath, as a set. */
private final HashSet<String> classpathElementsSet = new HashSet<>();

/** Clear the classpath. */
private void clearClasspath() {
    classpathElements.clear();
    classpathElementsSet.clear();
}

/** Add a classpath element. */
private void addClasspathElement(String pathElement) {
    if (classpathElementsSet.add(pathElement)) {
        final File file = new File(pathElement);
        if (file.exists()) {
            classpathElements.add(file);
        }
    }
}

/** Parse the system classpath. */
private void parseSystemClasspath() {
    // Look for all unique classloaders.
    // Keep them in an order that (hopefully) reflects the order in which class resolution occurs.
    ArrayList<ClassLoader> classLoaders = new ArrayList<>();
    HashSet<ClassLoader> classLoadersSet = new HashSet<>();
    classLoadersSet.add(ClassLoader.getSystemClassLoader());
    classLoaders.add(ClassLoader.getSystemClassLoader());
    if (classLoadersSet.add(Thread.currentThread().getContextClassLoader())) {
        classLoaders.add(Thread.currentThread().getContextClassLoader());
    }
    // Dirty method for looking for any other classloaders on the call stack
    try {
        // Generate stacktrace
        throw new Exception();
    } catch (Exception e) {
        StackTraceElement[] stacktrace = e.getStackTrace();
        for (StackTraceElement elt : stacktrace) {
            try {
                ClassLoader cl = Class.forName(elt.getClassName()).getClassLoader();
                if (classLoadersSet.add(cl)) {
                    classLoaders.add(cl);
                }
            } catch (ClassNotFoundException e1) {
            }
        }
    }

    // Get file paths for URLs of each classloader.
    clearClasspath();
    for (ClassLoader cl : classLoaders) {
        if (cl != null) {
            for (URL url : ((URLClassLoader) cl).getURLs()) {
                if ("file".equals(url.getProtocol())) {
                    addClasspathElement(url.getFile());
                }
            }
        }
    }
}

/** Override the system classpath with a custom classpath to search. */
public FastClasspathScanner overrideClasspath(String classpath) {
    clearClasspath();
    for (String pathElement : classpath.split(File.pathSeparator)) {
        addClasspathElement(pathElement);
    }
    return this;
}

/**
 * Get a list of unique elements on the classpath (directories and files) as File objects, preserving order.
 * Classpath elements that do not exist are not included in the list.
 */
public ArrayList<File> getUniqueClasspathElements() {
    return classpathElements;
}

For future reference, in case you need to pass in the class path to ProcessBuilder:

StringBuffer buffer = new StringBuffer();
for (URL url :
    ((URLClassLoader) (Thread.currentThread()
        .getContextClassLoader())).getURLs()) {
  buffer.append(new File(url.getPath()));
  buffer.append(System.getProperty("path.separator"));
}
String classpath = buffer.toString();
int toIndex = classpath
    .lastIndexOf(System.getProperty("path.separator"));
classpath = classpath.substring(0, toIndex);
ProcessBuilder builder = new ProcessBuilder("java",
    "-classpath", classpath, "com.a.b.c.TestProgram");

In case other answers don't work, try this:

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
    System.out.println(url.getFile());
}

Drop this code into an empty jsp page to view classLoader hierarchy and associated jars loaded at each level.

visit() method below could also be used on its own

<%!
    public void visit(StringBuilder sb, int indent, ClassLoader classLoader) {
        if (indent > 20 || classLoader == null)
            return;
        String indentStr = new String(new char[indent]).replace("\0", "    ");
        sb.append("\n");
        sb.append(indentStr);
        sb.append(classLoader.getClass().getName());
        sb.append(":");
        if (classLoader instanceof java.net.URLClassLoader) {
            java.net.URL[] urls = ((java.net.URLClassLoader)classLoader).getURLs();
            for (java.net.URL url : urls) {
                sb.append("\n");
                sb.append(indentStr);
                sb.append(url);
            }
        }
        sb.append("\n");
        visit(sb, indent + 1, classLoader.getParent());
    }

%>

<%
StringBuilder sb = new StringBuilder();
visit(sb,1,this.getClass().getClassLoader());
%>
<pre>
<%=sb%>
</pre>

Need Your Help

wpf tooltip on mouseover and mouseout

wpf tooltip mouseevent

What I am trying to do is have the tool tip show once a mouse over occurs. The ToolTip will not turn off until a mouse out.

if else condition in ggplot to add an extra layer

r if-statement ggplot2

say I want to plot two layers in ggplot, one containing points and another one containing lines if a certain criteria is fulfilled.