¿Cómo usar javax.tools.ToolProvider desde un cargador de clases personalizado?
compiler-construction casting (4)
Parece imposible utilizar javax.tools.ToolProvider
desde un cargador de clases personalizado como lo requieren Ant o Webstart: http://bugs.sun.com/view_bug.do?bug_id=6548428
javax.tools.ToolProvider.getSystemJavaCompiler()
carga javax.tools.JavaCompiler
en un URLClassLoader cuyo padre es el cargador de clases del sistema. La API no parece permitir a los usuarios especificar un cargador de clases principal.
¿Cómo se puede usar javax.tools.JavaCompiler
desde un cargador de clases personalizado?
Por ejemplo:
- Ant cargas
MyParserTask
-
MyParserTask
analiza el código fuente de Java -
MyParserTask
es cargado porAntClassLoader
que delega en el cargador de clases del sistema -
javax.tools.JavaCompiler
es cargado porURLClassLoader
y delegado en el cargador de clases del sistema
En un momento posterior, MyParserTask
invoca:
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
- Al ver cómo residen las dos clases en cargadores de clases separados, no parece haber una manera para que
MyParserTask
interactúe conJavacTask
sin obtener erroresClassCastException
.
¿Algunas ideas?
Estaba teniendo problemas similares. Tenía que cargar las herramientas. Jar Cuando descubrí que no se estaba cargando una subclase. detalles en este enlace https://.com/questions/14619179/webappclassloader-loadclass-cannot-find-class-at-runtime Como menciono, esta puede no ser una buena solución, ya que estamos tratando de jugar con la carga de clases a través de un programa. algunas notas aquí también son útiles http://easternlights-wisdomtree.blogspot.in/2013/02/classloading-blues-part1.html
Este problema ocurre frecuentemente con OSGi. Algunas personas han creado "cargadores de clase puente", consulte, por ejemplo, este artículo (que probablemente solo une interfaces, no subclases, por lo que quizás no pueda usarlo directamente).
Si solo hay unos pocos métodos que desea invocar en el objeto "extraño", también puede salirse con la suya:
javax.tools.CompilationTask task;
task.getClass().getMethod("someMethodInTheSubclassThatICannotSee").invoke("a");
Sobre la base de la idea de reflexión, tal vez también sea útil un lenguaje de scripts (Groovy, Beanshell, JavaScript).
La respuesta simple es que la misma clase cargada por dos cargadores de clases diferentes es un tipo diferente y nunca los dos serán asignables cruzadamente. Eso es. Debes tener ambas clases para usar el mismo cargador de clases para obtener la clase compartida.
Esto normalmente sería el resultado de la violación del aplazamiento preventivo de la carga de clase a un padre de ClassLoader. En pocas palabras, cualquier cargador de clases primero debe pedirle a su padre que cargue una clase antes de intentar cargarlo. Hacer lo contrario da lugar a todo tipo de problemas "interesantes".
En su ejemplo específico, dado que A invocó B, fue el cargador de clases de B que no pudo delegar en su padre, ya que si A puede ver la clase objetivo, el cargador de clases de B no necesitó cargarlo, dado que A invocó B y por lo tanto A cargador de clase o algún antecesor del mismo cargado B.
Tuve exactamente el mismo problema. Estoy usando una tarea ant personalizada para escanear el AST para ciertos tipos de invocaciones de métodos. Mi solución, que puede no ser adecuada para usted, fue crear una instancia del compilador en lugar de utilizar el ToolProvider.
Reemplacé
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
con
JavaCompiler compiler = (JavaCompiler)Class.forName("com.sun.tools.javac.api.JavacTool").newInstance();
Esto ciertamente no es a prueba de futuro o seguro en todos los entornos, pero es una opción que depende de sus necesidades. Si alguien más tiene una mejor forma de utilizar el ToolProvider en las tareas Ant personalizadas, comparta.