Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 573
Phil Avatar asked Nov 08 '08 06:11

Phil


2 Answers

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.

like image 57
Leihca Avatar answered Nov 08 '22 08:11

Leihca


Another option is to use Commons JCI.

like image 1
tcurdt Avatar answered Nov 08 '22 09:11

tcurdt