Как мне использовать 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 (который позволяет вам получить путь к классу), но вы можете вставить свой собственный путь к классу, если хотите.
Вы задаете два отдельных вопроса здесь.
Один из них - как скомпилировать классы, которых нет в системном пути к классам. Это легко решается передачей аргумента командной строки "-classpath" компилятору (как впервые упомянул Leihca).
Второй - как создать экземпляр ToolProvider и JavaCompiler в загрузчике классов контекста потока. На момент написания этой статьи это нерешенный вопрос: использовать javax.tools.ToolProvider из пользовательского загрузчика классов?