简体   繁体   中英

How do I use JDK6 ToolProvider and JavaCompiler with the context classloader?

My usage case is compiling generated source files from a java program using the ToolProvider and JavaCompiler classes provided in JDK 6. The source files contain references to classes in the context classloader (it runs in a J2EE container), but not in the system classloader. My understanding is that by default the ToolProvider will create the JavaCompiler instance with the system classloader.

Is there a way to specify a classloader for JavaCompiler to use?

I tried this approach, modified from something on IBM DeveloperWorks:

FileManagerImpl fm = 
    new FileManagerImpl(compiler.getStandardFileManager(null, null, null););

with FileManagerImpl defined as:

static final class FileManagerImpl 
    extends ForwardingJavaFileManager<JavaFileManager> {

   public FileManagerImpl(JavaFileManager fileManager) {
      super(fileManager);
   }

   @Override
   public ClassLoader getClassLoader(JavaFileManager.Location location) {
      new Exception().printStackTrace();
      return Thread.currentThread().getContextClassLoader();
   }

}

The stacktrace indicates it's only called once during annotation processing. I verified the class referenced in the source file to be compiled is not on the system classpath but is available from the context classloader.

If you know the classpath to the files that are known to the contextclassloader you can pass them to the compiler:

    StandardJavaFileManager fileManager = compiler.getStandardFileManager(this /* diagnosticlistener */, null, null);
// get compilationunits from somewhere, for instance via fileManager.getJavaFileObjectsFromFiles(List<file> files)
List<String> options = new ArrayList<String>();
options.add("-classpath");
StringBuilder sb = new StringBuilder();
URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
for (URL url : urlClassLoader.getURLs())
    sb.append(url.getFile()).append(File.pathSeparator);
options.add(sb.toString());
CompilationTask task = compiler.getTask(null, fileManager, this /* diagnosticlistener */, options, null, compilationUnits);
task.call();

This example assumes you're using a URLClassloader (which allows you to retrieve the classpath) but you could insert your own classpath if you wanted to.

另一个选择是使用Commons JCI

You're asking two separate questions here.

One is how to compile classes not found in the system classpath. This is easily solved by passing the "-classpath" command-line argument to the compiler (as first mentioned by Leihca).

The second is how to instantiate ToolProvider and JavaCompiler on the thread context classloader. At the time of this writing, this is an unsolved question: Using javax.tools.ToolProvider from a custom classloader?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM