Reputation: 90023
It seems to be impossible to use javax.tools.ToolProvider
from a custom classloader as required by Ant or Webstart: https://bugs.java.com/bugdatabase/view_bug?bug_id=6548428
javax.tools.ToolProvider.getSystemJavaCompiler()
loads javax.tools.JavaCompiler
into a URLClassLoader whose parent is the system classloader. The API does not seem to allow users to specify a parent classloader.
How can one use javax.tools.JavaCompiler
from a custom classloader?
For example:
MyParserTask
MyParserTask
parses Java source-codeMyParserTask
is loaded by AntClassLoader
that delegates to the system classloaderjavax.tools.JavaCompiler
is loaded by URLClassLoader
thast delegates to the system classloaderAt a later point, MyParserTask
invokes:
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
to interact with JavacTask
without getting ClassCastException
errors.Any ideas?
Upvotes: 6
Views: 3221
Reputation: 1
I was having similar problems I had to load the tools.jar When I discovered that a sub class was not getting loaded. details in this link https://stackoverflow.com/questions/14619179/webappclassloader-loadclass-cannot-find-class-at-runtime As I mention this may not be a good solution as we are trying to tinker with the classloading through a program. found some notes here useful as well http://easternlights-wisdomtree.blogspot.in/2013/02/classloading-blues-part1.html
Upvotes: 0
Reputation: 51
I had exactly the same problem. I'm using a custom ant task to scan the AST for certain kinds of method invocations. My solution, which may not be appropriate for you, was to instantiate the compiler myself instead of using the ToolProvider.
I replaced
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
with
JavaCompiler compiler = (JavaCompiler)Class.forName("com.sun.tools.javac.api.JavacTool").newInstance();
This certainly isn't future proof or safe in all environments, but it is an option depending on your needs. If someone else has a better way around using ToolProvider in custom Ant tasks please share.
Upvotes: 2
Reputation: 64026
The simple answer is that the same class loaded by two different class loaders is a different type and never the twain shall be cross assignable. That's it. You have to have both classes use the same class loader to get the shared class.
This would usually be a result of violating the pre-emptive deferring of class loading to a ClassLoader's parent. Simply put, any class loader must first ask it's parent to load a class before it attempts to load it itself. Doing otherwise results in all sorts of "interesting" problems.
In your specific example, since A invoked B, it was B's class loader that failed to delegate to it's parent, since if A can see the target class, B's class loader did not need to load it, given that A invoked B and therefore A's class loader or some ancestor thereof loaded B.
Upvotes: 0
Reputation: 262504
This problem frequently occurs with OSGi. Some people have come up with "bridge class loaders", see for example this article (which probably only bridges interfaces, not subclasses, so maybe you cannot use it directly).
If there are only a few methods you want to invoke on the "foreign" object, you can also get away with reflection:
javax.tools.CompilationTask task;
task.getClass().getMethod("someMethodInTheSubclassThatICannotSee").invoke("a");
Building on the reflection idea, maybe a scripting language is helpful, too (Groovy, Beanshell, JavaScript).
Upvotes: 0