views:

1207

answers:

3

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.

+1  A: 

Another option is to use Commons JCI.

tcurdt
It doesn't appear that JCI allows you to pass a classloader in, only paths, just as the JDK6 support allows.
Phil
Where have you been looking? Sure it does.
tcurdt
+1  A: 

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.

Leihca
A: 

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: http://stackoverflow.com/questions/2315719/how-to-interact-with-javax-tools-toolprovider-outside-the-system-classloader

Gili