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?
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.
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).
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.
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
Related
I have a class A extends B.
I've created a CustomClassLoader extends ClassLoader to use defineClass(className, byte[], offset, length).
I've instanciate a new CustomClassLoader(Thread.currentThread().getContextClassLoader()).
So the parent of my CustomClassLoader is the ClassLoader from the current thread.
I've modified B class using ASM framework. I've write my modified class in a .class file and use a decompiler to be sure it works. And it works.
I've added modified B class to my CustomClassLoader
I've set the Thread.currentThread().setContextClassLoader() with my CustomClassLoader.
I've load A using Class.forName(String, true, the CustomClassLoader).
But the loaded B class seems to be the orginal class.
What did I wrong ?
If you need more info, a detailed topic is on my GitHub.
Java classloaders first search the parent classloader before looking in the child.
The loadClass method in ClassLoader performs these tasks, in order,
when called to load a class:
If a class has already been loaded, it returns it.
Otherwise, it delegates the search for the new class to the parent class loader.
If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.
(Understanding Extension Class Loading - Oracle)
If you want to change that order, you need to override the loadClass method as well but there are many caveats and it's not advisable unless you understand classloading very well.
The easier option is to make sure that the parent class loader cannot find the original class B.
There are several things to know:
For most things, dealing with the thread’s context class loader is obsolete, as it has no impact. It’s more like a convention; setting it has an impact if there’s some other code querying and using it. For the standard class loading process, it doesn’t have any meaning. It’s unfortunate that the documentation doesn’t mention that and make it look like a relevant thing. Perhaps, it was intended to have more meaning when it was added.
As pointed out by Erwin Bolwidt, when loading A via your custom loader, it will delegate to its parent loader, returning a class A loaded by the parent.
When resolving class references, the JVM will always use the defining loader of the referrer. So when the reference from A to B is resolved, the JVM will always use the parent loader which defined the class A
The last point implies that even if you modify your custom class loader to look up its own classes first instead of following the standard model of querying the parent first, it doesn’t solve the issue if it has no own A, as then, it still returns the parent’s A whose references will be resolved using the parent. Since you are invoking defineClass before asking for A, the lookup order doesn’t matter at all, as your custom loader has an already defined B that it returned if anyone ever asked it for B…
So you could let your custom loader also load and define A. Or you use Reflection with access override to defineClass on the system ClassLoader before it loads B. The cleanest solution is to implement the class modification logic as a Java Agent which can use the Instrumentation API to intercept and change the definition of B right at its loading time.
I am using this code from an old IBM blog post about how to compile and use Java classes at runtime. The code mostly works great (and is quite well written, by the way), but unfortunately for me, it won't work in one of my use cases where the class being compiled refers to another class that can only be provided by the classLoader provided to the CharSequenceCompiler (from the blog post), not by the application classLoader.
To be more specific, The ClassLoader I pass into CharSequenceCompiler is a OSGi classLoader.
The bundle that owns this classLoader can find and return a class, say Foo.
class Foo { public static String FOO = "F"; }
I know this works if you do classLoader.findClass("Foo"); because when I call this from the debugger it works.
Now, from the class I compile at runtime, say Dynamic, I need to use Foo... so I pass the Foo bundle's ClassLoader to CharSequenceCompiler, then ask it to compile Dynamic:
class Dynamic { public static String D = Foo.FOO; }
This causes the following error:
error: cannot find symbol
Foo.FOO;
^
symbol: variable Foo
If Foo is in the same project as CharSequenceCompiler, then it works... so it's clearly a problem loading the class from the right class loader.
I have debugged this code for days (or evenings, tbh) and can't find out why the classLoader I provide to the compiler never even gets asked about this class...
The FileManager is asked to list() the resources in each package, but even when I use the debugger to manually add the FileObject to the returned list, it still won't work.
As the debugger cannot penetrate the native classes used by javac internally, I cannot progress anymore on this... does anyone have inside knowledge of the compiler that could explain what's going on?
I have figured this out after a long battle.
The problem with pretty much all implementations I found of in-memory java compilers based on the java.tools API is that, even though they let you pass in a ClassLoader to load the classes you compile, the classLoader is used only to load new classes, but not to obtain classes that can be used in the Java code being compiled.
For this reason, my use case (as explained in the question) would not work with the code shown in the IBM blog post (or with other projects like OpenHFT Java-Runtime-Compiler).
If you want the classes loaded by the ClassLoader you give to the compiler (which are not visible in the application ClassLoader) to be usable by the class being compiled, you need two things.
First, the ClassLoader classes must be enumerable. This can then be used by the JavaFileManager to implement the list() method properly. For each package, this method must return which classes the ClassLoader could load if required.
Secondly, you need to be able to build JavaFileObjects from the ClassLoader resources, because this is the type of the objects you must return. To do this, you need to ask the ClassLoader for the class's bytecode stream (use getResourceAsStream("Class.class")) and then just create a JavaFileObjectImpl. Basically, this in pseudo-code:
fileObject = new JavaFileObjectImpl( pathMinusDotClass, JavaFileObject.Kind.CLASS );
fileObject.openOutputStream()
.write( classLoader.getInputStream( path ) );
Now, the compiler will know which classes it can load from the provided classLoader and everything works.
I implemented this in a real compiler on my OSGiaaS project, which is not released yet but I plan to do it soon (writing in July 2016)... it includes a Java command in its shell which can run arbitrary Java code, and that's why I needed to get this working.
What is the benefits of using real java compiler. Isn't byte code generation option for you ?
In example: Byte Buddy or cglib
From this question, I found that it is not possible to look up a class from a sun.misc.DelegatingClassLoader, i.e. looking up a class on its own class loader like
Class<?> accessor = ...
accessor.getClassLoader().findClass(accessor.getName());
throws a ClassNotFoundException. The delegating class loader is used for loading runtime-generated accessor classes for converting Java's reflective JNI calls into Java invocations.
For some strange reason, I am not able to find the source of the DelegatingClassLoader anywhere in the JDK sources even though it is clearly available in my build where it seems to be an empty implementation of a subclass of the standard ClassLoader from looking at the class's byte code.
If the DelegatingClassLoader is however really only a simple subclass, I do not understand why it is not possible to look up a class by its name. Is there some VM magic involved? It seems like the
private native final Class<?> findLoadedClass0(String name);
method does not return the loaded class for a DelegatingClassLoader. The private classes field does however include the loaded class.
I was not able to find any information on how these class loaders are supposed to be different from any other class loaders. I am looking for an explanation why the above lookup does not work but throws an exception.
The source for DelegatingClassLoader is for whatever reason located in ClassDefiner.java. Interestingly, this code has a comment that references bug 4474172, which suggests that this class loader is known to the JVM and has special behavior:
The second solution is to make the virtual machine "magically"
delegate class loading for these newly-fabricated class loaders to
their parent loaders, without making an upcall to Java. The
disadvantage is that this is another hack in the JVM [...]. This
option has been chosen.
I am writing code that adds functions to a 'mod' if it exists in the classpath (referenced by pixelmonPresent)
PixelHammerTool extends ItemHammer
, ItemHammer only exists if pixelmon is present
The issue im having is, if i do this in the class (same package)
if(Basemod.pixelmonPresent) {
rubyHammer = new PixelHammerTool(Basemod.RUBY, "pixelutilitys:RubyHammer", "rubyHammer");
}
It will cause a class not found on PixelHammerTool,
Why is this being called if the if statement is false and what can i do about it ?
The why is simple and straightforward: because when a class is loaded, all the classes referenced by it are loaded too. (In fact they are loaded first.)
Avoiding it isn't too complicated either, although the code won't look nice: you need to load the class with reflection, using Class.forName(), find the constructor you want from the array returned by Class.getConstructors() and then create an instance using Constructor.newInstance().
Note that while if it only happens a few times in your code, this solution is fine, if you find yourself doing this a lot then you should probably look for a dependency injection framework that will do the heavy lifting for you.
Under the Linking section in the specs, we see this:
For example, a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized. Whichever strategy is followed, any error detected during resolution must be thrown at a point in the program that (directly or indirectly) uses a symbolic reference to the class or interface.
So when the constant has to be defined is implementation-dependent, based on the class loader. The behavior you're seeing is consistent with the "eager" resolution mentioned: when you reference PixelHammerTool in your code, even if it's for a runtime path that will never be hit, the class loader tries to link in its definition, which does not exist.
This strategy causes the JVM to start slower but execute faster at runtime, which is generally the strategy taken in all the implementations I'm familiar with. Indeed, the default class loader is given the name "bootstrap class loader" because it has this behavior - load classes at JVM bootstrap time.
You can either instantiate the class via reflection, as biziclop suggested (the easier route), which forces linking at runtime, or find or create a class loader that instantiates classes lazily.
I understand that java.lang.ClassLoader is generally the classloader I extend (when needed). Also, when I see the custom classloaders of tomcat, Jetty etc extend from java.lang.ClassLoader.
I'm curious to understand the purpose and usage of com.sun.org.apache.bcel.internal.util.ClassLoader.
Can someone help me understand it ?
com.sun.org.apache.bcel.internal is just a repackaging of Apache BCEL. In particular, the class we're interested in (ClassLoader) is documented here...
Drop in replacement for the standard class loader of the JVM. You can use it in conjunction with the JavaWrapper to dynamically modify/create classes as they're requested.
This class loader recognizes special requests in a distinct format, i.e., when the name of the requested class contains with "$$BCEL$$" it calls the createClass() method with that name (everything before the $$BCEL$$ is considered to be the package name. You can subclass the class loader and override that method. "Normal" classes class can be modified by overriding the modifyClass() method which is called just before defineClass().
judging by the "bcel" part of the package name that classloader doesnt just load classes, it alters the bytecode on the fly (see the bcel homepage).
edit: some more info on what they do with it can be found here : "BCEL is used internally by XSLTC to "compile" XSLT stylesheets into bytecodes for execution"