2012-07-23 10 views
38

Sto usando un codice di terze parti che quando viene fornito un argomento della riga di comando '-classpath' non imposta java.class.path, ma crea semplicemente un classloader, aggiunge tutti gli URL per gli elementi nella riga di comando percorso di classe specificato a il classloader e quindi lo imposta come il classloader di contesto. In una classe plugin di questo codice che ho scritto, ottengo un'istanza di questo classloader e in qualche modo ho bisogno di usarlo per recuperare il classpath sottostante, in modo che possa usarlo in un'invocazione di JavaCompiler.getTask (.. .) e compilare qualche altro codice al volo. Tuttavia non sembra esserci comunque per ottenere ClassPath da ClassLoader, e poiché java.class.path non è impostato, non riesco ad accedere al classpath sottostante a cui l'applicazione è stata inizialmente invocata con ... Qualsiasi idea ?Come ottenere classpath da classloader?

risposta

50

Se il classloader utilizza URL, deve essere un URLClassloader. Quello a cui si ha accesso è l'URL che definisce il classpath per esso insieme al genitore ClassLoader.

Per ottenere gli URL, è sufficiente effettuare le seguenti operazioni:

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

ha funzionato, grazie :) –

+0

Sei il benvenuto! –

7

Per riferimento futuro, nel caso in cui è necessario passare nel percorso di classe per 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"); 
5

Nel caso in cui altre risposte don 't lavoro, provate questo:

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

Le altre risposte sono corrette nella maggior parte delle situazioni, ma diventa più complicato di così in alcune impostazioni: ad es. Maven, Tomcat e JUnit dispongono del proprio supporto del classpath e non utilizzano il classpath di sistema.

Finora questo è il sistema più completo sono riuscito a venire con (questo codice è dal mio progetto Fast Classpath Scanner):

/** 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; 
} 
+1

Sicuramente la risposta più completa e in continua evoluzione se segui il progetto. –

+0

Quel metodo sporco che cerca qualsiasi altro classloader nello stack delle chiamate era esattamente ciò di cui avevo bisogno! Grazie mille! – FelipeKunzler

+0

@ luke-hutchison la tua biblioteca è un risparmiatore di vita! Per chiunque desideri una soluzione che ottenga classpath per una varietà di ambienti di runtime, questo è il migliore in circolazione. – Dan

0

goccia di questo codice in una pagina jsp vuota per visualizzare gerarchia dei classloader e associato vasi caricati ad ogni livello.

visita() il metodo di seguito potrebbe anche essere utilizzato da solo

<%! 
    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>