Как мне использовать JDK6 ToolProvider и JavaCompiler с загрузчиком классов контекста?

Мой пример использования - компиляция сгенерированных исходных файлов из java-программы с использованием классов ToolProvider и JavaCompiler, представленных в JDK 6. Исходные файлы содержат ссылки на классы в контекстном загрузчике классов (он выполняется в контейнере J2EE), но не в системном загрузчике классов. Насколько я понимаю, по умолчанию ToolProvider создает экземпляр JavaCompiler с системным загрузчиком классов.

Есть ли способ указать загрузчик классов для использования JavaCompiler?

Я попробовал этот подход, модифицированный от чего-то на IBM DeveloperWorks:

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

с FileManagerImpl, определенным как:

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();
   }

}

Stacktrace указывает, что он вызывается только один раз во время обработки аннотации. Я убедился, что класс, на который ссылается исходный файл для компиляции, находится не в системном пути к классам, а доступен из контекстного загрузчика классов.

3 ответа

Если вы знаете путь к классам для файлов, которые известны для contextclassloader, вы можете передать их компилятору:

    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();

В этом примере предполагается, что вы используете URLClassloader (который позволяет вам получить путь к классу), но вы можете вставить свой собственный путь к классу, если хотите.

Другой вариант - использовать Commons JCI.

Вы задаете два отдельных вопроса здесь.

Один из них - как скомпилировать классы, которых нет в системном пути к классам. Это легко решается передачей аргумента командной строки "-classpath" компилятору (как впервые упомянул Leihca).

Второй - как создать экземпляр ToolProvider и JavaCompiler в загрузчике классов контекста потока. На момент написания этой статьи это нерешенный вопрос: использовать javax.tools.ToolProvider из пользовательского загрузчика классов?

Другие вопросы по тегам