Hello all and thanks for the attention! I have a problem that must both be easy and obvious, yet I am stuck.
I want to deliver dynamically created Java classes to be used by a 3rd party library via a custom ClassLoader.
Now my problem is: How do I set my custom ClassLoader to be used to load this classes when I do not load them directly myself?
I thought when I used my ClassLoader to load a certain class, it became this class's ClassLoader, and all classes loaded from that class would be channeled through my ClassLoader.
I created a custom ClassLoader, following this official tutorial: http://java.sun.com/developer/onlineTraining/Security/Fundamentals/magercises/ClassLoader/help.html.
public class DynamicClassloader extends ClassLoader {
private Map<String, Class<?>> classesMap = new HashMap<String, Class<?>>();
public DynamicClassloader(ClassLoader parent) {
// Also tried super(parent);
super(sun.misc.Launcher.getLauncher().getClassLoader());
}
// Adding dynamically created classes
public void defineClass(String name, Class<?> clazz) {
classesMap.put(name, clazz);
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// load from parent
Class<?> result = findLoadedClass(name);
if (result != null) {
return result;
}
try {
result = findSystemClass(name);
} catch (Exception e) {
// Ignore these
}
if (result != null) {
return result;
}
result = classesMap.get(name);
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
}
I wanted to use this somewhere else in the code like that:
ClassLoader thisClassLoader = this.getClass().getClassLoader();
((DynamicClassloader) thisClassLoader).defineClass(className, dynClass);
Now my problem is that when I call findSystemClass(name) of the 3rd party library class, the parent ClassLoader finds this class (because it is on the classpath) and becomes its ClassLoader. And since the parent ClassLoader doesn't know about my custom ClassLoader, it is effectively been put out of use and this.getClass().getClassLoader() cannot be cast to DynamicClassLoader.
Another approach would be to set my ClassLoader to be the system ClassLoader via JVM argument -Djava.system.class.loader=my.DynamicClassloader. But that gives me a StackOverflowError:
...
at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.java:39)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.ClassLoader.findSystemClass(ClassLoader.java:916)
at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.java:39)
...
This must be really easy to do, yet I am now out of ideas... any help is greatly appreciated!
Not sure I understand the question, you have a 3rd party lib and you want it to use your classloader to load classes.
If you're lucky the third party lib uses the threads context classloader which you can set using Thread.currentThread().setContextClassLoader(myClassLoader), in the same thread you can access this classloader with Thread.currentThread().getContextClassLoader()...
Another point, but not sure it is important in your context, is that you can also write a parent-last classloader that will try to load the class before delegating to its parent (instead of trying to delegate first)
Edited after your comment:
parent_last classloader will make a difference if your library doesn't rely on the thread context classloader, then you have to load the library with your parent-last classloader thus setting your classloader as the classloader for the library instead of its parent loader (the parent of your classloader)...
You may also make a classloader with a parent-first behavior but for your 3rd party library...
And a good link about classloaders...
Related
I know that Class instance loaded by different class loader can't be cast to each other.
But what if the one Class extends the other? I did an experiment and the result is confusing. Here is the ClassLoader I define:
public class MyClassLoader extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
if (name.startsWith("java")) {
return super.loadClass(name);
}
String filename = "/" + name.replaceAll("\\.", "/") + ".class";
InputStream is = getClass().getResourceAsStream(filename);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (Throwable e) {
throw new ClassNotFoundException(name);
}
}
}
And the experiment code:
// These classes will be loaded by MyClassLoader
class Parent { }
class Child extends Parent { }
class MyCalendarData_aa_DJ extends CalendarData_aa_DJ { }
class MyAppleScriptEngine extends AppleScriptEngine { }
class MyBufferedReader extends BufferedReader {
public MyBufferedReader(Reader in) {
super(in);
}
}
public class DifferentClassLoaderCast {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new MyClassLoader();
Class<?> pClass = classLoader.loadClass(Parent.class.getName());
Class<?> cClass = classLoader.loadClass(Child.class.getName());
// true, as pClass and cClass are loaded by same classloader
System.out.println(pClass.isAssignableFrom(cClass));
// false, different classloader
System.out.println(Parent.class.isAssignableFrom(cClass));
// true, why?
System.out.println(Object.class.isAssignableFrom(pClass));
Class<?> myCalendarData_aa_DJClass = classLoader.loadClass(MyCalendarData_aa_DJ.class.getName());
// false, CalendarData_aa_DJ is loaded by JAVA ext-classloader
System.out.println(CalendarData_aa_DJ.class.isAssignableFrom(myCalendarData_aa_DJClass));
Class<?> myAppleScriptEngine = classLoader.loadClass(MyAppleScriptEngine.class.getName());
// false, why? AppleScriptEngine is loaded by JAVA bootstrap-classloader
System.out.println(AppleScriptEngine.class.isAssignableFrom(myAppleScriptEngine));
Class<?> myBufferedReader = classLoader.loadClass(MyBufferedReader.class.getName());
// true, why? BufferedReader is loaded by JAVA bootstrap-classlaoder
System.out.println(BufferedReader.class.isAssignableFrom(myBufferedReader));
}
}
It seems that subclass loaded by MyClassLoader can be cast to superclass loaded by bootstrap class loader under package starts with java or built-in class?
// true, why?
System.out.println(Object.class.isAssignableFrom(pClass));
this one should be entirely obvious. Object is java.lang.Object and you rather clumsily call super.loadClass if the fully qualified name starts with java. Which means the loader of Object.class is the system loader, and this is true for all load ops: Whether classLoader loads Parent, or the system loader does, they both work off of the notion that j.l.Object.class is loaded by the system loader: The same type, therefore, compatible.
// false, why? AppleScriptEngine is loaded by JAVA bootstrap-classloader
System.out.println(AppleScriptEngine.class.isAssignableFrom(myAppleScriptEngine));
same reason. In reverse: the fully qualified name of AppleScriptEngine is not starting with "java".
Class<?> myBufferedReader = classLoader.loadClass(MyBufferedReader.class.getName());
// true, why? BufferedReader is loaded by JAVA bootstrap-classlaoder
System.out.println(BufferedReader.class.isAssignableFrom(myBufferedReader));
you guessed it. Because the FQN of BufferedReader starts with "java".
Perhaps you've misunderstood the classloading model.
The model that classloaders employ is a parent/child relationship. A classloader has a parent.
Any class is loaded by some classloader; if it hits any other class in its source code it will ask its own classloader to load it. But that loader may defer the job to any other loader. That's important. Your code will defer for any class whose FQN starts with "java" (and not even "java.", which is a peculiar choice). Otherwise, it loads itself. The classloader that is on record as THE loader of a class is the one that invoked defineClass. In your code, if you go via the if block that checks for starting with "java", your loader does NOT invoke defineClass, and therefore isn't the loader. If that if is not taken, you always end up invoking defineClass, making you the loader.
The common model for classloaders is this:
Ask your parent(s) to load the class, in order. If it can, great. We return that result, and that means the loader of said class is the parent and not you!
If not, then this loader will load it. Conflicts are unlikely; after all, the system loader couldn't even find it. Now you are the loader.
ClassLoader itself supports this model, but you get it by overriding findClass and NOT loadClass. The default impl of loadClass will do precisely as above: First calls the parents' loadClass methods, and only if those can't find it, will it invoke findClass to finish the job.
I strongly recommend you follow this flow, and update your code to extend findClass, not loadClass.
If you really want to load it yourself and NOT delegate to your parent loaders, then, yeah, overriding loadClass is how you do it. But now you have to deal with the fact that if it is a class that your parent can also find, that you can run into the scenario where your loader loaded, say, com.foo.Example, and parent did too, and whilst those classes have exactly the same name, as far as the JVM is concerned, they are completely unrelated and entirely incompatible with each other. The JVM doesn't mind, but it leads to highly confusing scenarios, where an object of type com.foo.Example cannot be assigned to a variable of type... com.foo.Example.
If you must do this, note that checking if it starts with "java" is highly suboptimal. For starters, "java." is a better fit, and for seconds, not all system classes start with "java". Ask the system loader first, if it can load it, defer to that (just return what it found), at the very least.
What are you trying to accomplish by writing a loader? With that insight, I can give more advice on which method (loadClass or findClass) is appropriate to override.
I am trying to force some of the classes to be loaded using my custom class loader, the problem is after the loading the calling class still doesn't know about the class definition and tries to load it again, of course after that we have two different definitions of the class and assigning one to the other results in class cast exception.
Any pointers or ideas how this can be fixed?
This is the calling class:
CustomClassLoader loader = new CustomClassLoader(this.getPackageCodePath());
Class<?> midletClass = loader.loadClass(className);
midletClass.getMethod("InitEngine", Class.forName("android.app.Activity")).invoke(null, this);
ms_MIDlet = (MIDlet)midletClass.newInstance();//ClassCastException
And this is the class loader itself
public class CustomClassLoader extends PathClassLoader
{
public CustomClassLoader(String path)
{
super(path, getSystemClassLoader());//we set the parent to be the system class loader so the loading gets done in this class
}
#Override
public InputStream getResourceAsStream(String resName)
{
//...do some resource loading here
}
}
The calling class is running in some ClassLoader A. This classloader knows where to find and load MIDlet.class. Otherwise line 4 would produce a ClassNotFoundException for the cast.
You also use an instance of CustomClassLoader to load the Midlet.class.
On line 4 this blows up because you are casting the Midlet instance loaded by CustomClassLoader to a Midlet instances loaded by the ClassLoader A.
One solution is to make the CustomClassLoader logic delegate to ClassLoader A before loading a class itself. Something along the lines of first delegating a loadClass call to parentLoader.loadClass or getResourceAsStream to parentLoader.getResourceAsStream. If those calls fail then you can do your custom resource lookup.
This approach will make sure that all Midlet.class'es are actually loaded by the same classloader. You can check the delegation example at Java ClassLoader delegation model?
I'm trying to use Class.forName to dynamically load a class from a .jar file on the filesystem at runtime. The class I am trying to load implements an interface in another .jar file, so I am using my own URLClassLoader to reference the two .jars.
The code works when it is called not in the context of the web app (I have tested this by copying and pasting the method into a separate program and calling it from main). However, when I run/debug the web app (I'm using NetBeans) the code fails. The newInstance method throws a ClassCastException when I try to cast the instance to the interface specified in my jar_file_dependencies.jar.
Here is the relevant code if this helps:
File gameJar = new File("C:\\file_path\\jar_file.jar");
File gameDependenciesJar = new File("C:\\file_path\\jar_file_dependencies.jar");
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
});
Class clazz = Class.forName("MyClass", true, cl);
IMyClass myClass = (IMyClass)clazz.newInstance();
System.out.println(game);
} catch (Exception e)
{
System.out.println(e.getMessage());
}
Any suggestions as to why this code is working in one program and not another would be greatly appreciated.
Many thanks,
Dan
short answer without going into too many of the hairy details: one or both of the gameJar and gameDependenciesJar probably contain a definition of the IMyClass class/interface. the rule of thumb when using child classloaders is that the child classloader should not contain any of the "shared" classes--these should exist only in the parent classloader.
partial explanation: Web app classloaders usually have different delegation policies from normal classloaders. often they prefer the child's class to the parent's. normal classloaders generally prefer the parent's class to the child's. in your web app, you are ending up with 2 separate definitions of the IMyClass class (one def in the parent classloader, one in the child). in your normal app, the IMyClass definition in the child classloader is being ignored, so only one definition gets loaded (in the parent classloader), and everything is happy.
Maybe this will help, (untested):
ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
if (clsLoader == null) {
clsLoader = this.getClass().getClassLoader();
}
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
}, clsLoader);
Also, you should pass a full declarative name of the class MyClass instead of just calling it MyClass in Class.forName().
E.g.
Class clazz = Class.forName("com.xxxx.yyy.MyClass", true, cl);
I am developing a web application.
The web application generates java classes on the fly. For example it generates class com.people.Customer.java
In my code, I dynamically compile this to get com.people.Customer.class and store in some directory say repository/com/people/Customer.class which is not on the classpath of my application server.My application server(I am using WebSphere Application Server/Apache Tomcat etc) picks up the classes from the WEB-INF/classes directory. The Classloader would use this to load the classes.
After compilation I need to load this class so that it becomes accessible to other classes using it after its creation.
When I use Thread.currentThread().getContextClassLoader().loadClass(com.people.Customer) obviously the Classloader is not able to load the class, since its not on the classpath(not in WEB-INF/classes). Due to similar reasons, getResource(..) or getResourceAsStream(..) also does not work.
I need a way to :
Read the class Customer.class maybe as a stream (or any other way would do) and then load it. Following are the constraints:
I cannot add the repository folder to the WEB-INF/classes folder.
I cannot create a new Custom ClassLoader. If I create a new ClassLoader and this loads the class, it will not be accessible to its parent ClassLoader.
Is there any way of achieving this?
If not this, in the worse case, is there a way of overriding the default class loader with a custom class loader for web applications the same classloader should be used to load applications throughout entire lifecycle of my web application.
Appreciate any solution :)
You need a custom class loader to do this, and in this classloader you need to re-define a method findClass(String name)
An example:
public class CustomClassLoader extends ClassLoader {
final String basePath = "/your/base/path/to/directory/named/repository/";
#Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
String fullName = name.replace('.', '/');
fullName += ".class";
String path = basePath + fullName ;
try {
FileInputStream fis = new FileInputStream(path);
byte[] data = new byte[fis.available()];
fis.read(data);
Class<?> res = defineClass(name, data, 0, data.length);
fis.close();
return res;
} catch(Exception e) {
return super.findClass(name);
}
}
}
Then, you'll be load classes from custom location. For example:
Class<?> clazz = Class.forName("my.pretty.Clazz", true, new CustomClassLoader());
Object obj = clazz.newInstance();
Doing this, you tell JVM that class named my.pretty.Clazz should be loaded by your custom class loader, which knows how and where from to load your custom class.
It resolves full class name (like my.pretty.Clazz) to file name (in our case: /your/base/path/to/directory/named/repository/my/pretty/Clazz.class), then loads obtained resource as a byte array, and finally converts this array to a Class instance.
This example is very simple and demonstrates a general technique about how to load custom classes as in your case.
I suggest you to read some articles about class loading, for example this one.
Short answer: No
Without a custom ClassLoader, you cannot dynamically load classes.
However, your assumption that you cannot use a custom ClassLoader because your other objects loaded by the WebApp ClassLoader would be unable to use these newly loaded classes is incorrect. All you need is a generic way to use these newly created classes - like a common interface or a meta-description (Beans Introspector for accessing bean properties).
But if you are using third-party libraries like Hibernate and you are dynamically loading entities at runtime which are to be persisted, then you will have a hard time, but imho it is possible.
Sure you can do this. Just get the web classloader and call the defineClass() method using reflection (it is protected, so be sure to call setAccessible(true) on the method. defineClass() takes a byte array, so it doesn't make any difference where you class is from. Make sure that the class name is unique and you're loading it only once, or you'll have complicated classloading problems.
For debug reasons, and curiosity, I wish to list all classes loaded to a specific class loader.
Seeing as most methods of a class loader are protected, what is the best way to accomplish what I want?
Thanks!
Try this. It's a hackerish solution but it will do.
The field classes in any classloader (under Sun's impl since 1.0) holds hard reference to the classes defined by the loader so they won't be GC'd. You can take a benefit from via reflection.
Field f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes = (Vector<Class>) f.get(classLoader);
Instrumentation.getInitiatedClasses(ClassLoader) may do what you want.
According to the docs:
Returns an array of all classes for which loader is an initiating loader.
I'm not sure what "initiating loader" means though. If that does not give the right result try using the getAllLoadedClasses() method and manually filtering by ClassLoader.
How to get an instance of Instrumentation
Only the agent JAR (which is separate from the application JAR) can get an instance of the Instrumentation interface. A simple way to make it available to the application is to create an agent JAR containing one class with a premain method that does nothing but save a reference to the Instrumentation instance in the system properties.
Example agent class:
public class InstrumentHook {
public static void premain(String agentArgs, Instrumentation inst) {
if (agentArgs != null) {
System.getProperties().put(AGENT_ARGS_KEY, agentArgs);
}
System.getProperties().put(INSTRUMENTATION_KEY, inst);
}
public static Instrumentation getInstrumentation() {
return (Instrumentation) System.getProperties().get(INSTRUMENTATION_KEY);
}
// Needn't be a UUID - can be a String or any other object that
// implements equals().
private static final Object AGENT_ARGS_KEY =
UUID.fromString("887b43f3-c742-4b87-978d-70d2db74e40e");
private static final Object INSTRUMENTATION_KEY =
UUID.fromString("214ac54a-60a5-417e-b3b8-772e80a16667");
}
Example manifest:
Manifest-Version: 1.0
Premain-Class: InstrumentHook
The resulting JAR must then be referenced by the application and specified on the command line (with the -javaagent option) when launching the application. It might be loaded twice in different ClassLoaders, but that is not a problem since the system Properties is a per-process singleton.
Example application class
public class Main {
public static void main(String[] args) {
Instrumentation inst = InstrumentHook.getInstrumentation();
for (Class<?> clazz: inst.getAllLoadedClasses()) {
System.err.println(clazz.getName());
}
}
}