views:

288

answers:

2

It seems to be impossible to use javax.tools.ToolProvider from a custom classloader as required by Ant or Webstart: http://bugs.sun.com/view_bug.do?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:

  • Ant loads MyParserTask
  • MyParserTask parses Java source-code
  • MyParserTask is loaded by AntClassLoader that delegates to the system classloader
  • javax.tools.JavaCompiler is loaded by URLClassLoader thast delegates to the system classloader

At 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
  • Seeing how the two classes reside on separate classloaders, there doesn't seem to be a way for MyParserTask to interact with JavacTask without getting ClassCastException errors.

Any ideas?

A: 

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).

Thilo
A: 

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.

Software Monkey
I rewrote the question to focus on CompilationTask. Please take a look.
Gili