Использование javax.tools.ToolProvider из пользовательского загрузчика классов?

Кажется, невозможно использовать javax.tools.ToolProvider из пользовательского загрузчика классов в соответствии с требованиями Ant или Webstart: http://bugs.sun.com/view_bug.do?bug_id=6548428

javax.tools.ToolProvider.getSystemJavaCompiler() грузы javax.tools.JavaCompiler в URLClassLoader, чьим родителем является системный загрузчик классов. Похоже, API не позволяет пользователям указывать родительский загрузчик классов.

Как можно использовать javax.tools.JavaCompiler из пользовательского загрузчика классов?

Например:

  • Муравьиные грузы MyParserTask
  • MyParserTask разбирает исходный код Java
  • MyParserTask загружается AntClassLoader который делегирует системному загрузчику классов
  • javax.tools.JavaCompiler загружается URLClassLoader это делегаты системному загрузчику классов

Позже, MyParserTask вызывает:

javax.tools.CompilationTask task = compiler.getTask(...);
com.sun.source.util.JavacTask javacTask = (com.sun.source.util.JavacTask) task;
javacTask.parse().next().accept(visitor, unused); // parsing happens here
  • Видя, как эти два класса находятся на разных загрузчиках классов, похоже, нет возможности MyParserTask взаимодействовать с JavacTask не получая ClassCastException ошибки.

Есть идеи?

4 ответа

У меня была точно такая же проблема. Я использую пользовательскую задачу ant для сканирования AST для определенных видов вызовов методов. Мое решение, которое может не подходить для вас, состояло в том, чтобы создать экземпляр самого компилятора вместо использования ToolProvider.

Я заменил

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

с

JavaCompiler compiler = (JavaCompiler)Class.forName("com.sun.tools.javac.api.JavacTool").newInstance();

Это, конечно, не будущее и не безопасно во всех средах, но это вариант в зависимости от ваших потребностей. Если у кого-то есть лучший способ использовать ToolProvider в пользовательских задачах Ant, пожалуйста, поделитесь.

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

Обычно это происходит в результате нарушения преимущественной отсрочки загрузки класса для родителя ClassLoader. Проще говоря, любой загрузчик классов должен сначала попросить своего родителя загрузить класс, прежде чем он попытается загрузить его сам. В противном случае возникают всевозможные "интересные" проблемы.

В вашем конкретном примере, так как A вызвал B, это был загрузчик класса B, который не смог делегировать своему родителю, так как если A может видеть целевой класс, загрузчик класса B не должен был загружать его, учитывая, что A вызвал B и, следовательно, A загрузчик классов или какой-то его предок загружен B.

Эта проблема часто возникает с OSGi. Некоторые люди придумали "загрузчики классов моста", см., Например, эту статью (которая, вероятно, только соединяет интерфейсы, а не подклассы, поэтому, возможно, вы не можете использовать его напрямую).

Если есть только несколько методов, которые вы хотите вызвать для "чужого" объекта, вы также можете избежать размышлений:

 javax.tools.CompilationTask task;
 task.getClass().getMethod("someMethodInTheSubclassThatICannotSee").invoke("a");

Основываясь на идее рефлексии, возможно, язык скриптов тоже будет полезен (Groovy, Beanshell, JavaScript).

У меня были похожие проблемы, я должен был загрузить tools.jar Когда я обнаружил, что подкласс не загружается. подробности в этой ссылке https://stackru.com/questions/14619179/webappclassloader-loadclass-cannot-find-class-at-runtime Поскольку я упоминаю, что это может быть не очень хорошим решением, так как мы пытаемся возиться с загрузкой классов через программа. нашел некоторые заметки здесь также полезными http://easternlights-wisdomtree.blogspot.in/2013/02/classloading-blues-part1.html

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