cast across classloader?
How can I do this:
class Foo {
public static Foo get() throws Exception {
ClassLoader cl = new URLClassLoader(new URL[]{"foo.jar"}, null); // Foo.class is in foo.jar
return (Foo)cl.loadClass("Foo").newInstance(); // fails on class cast
}
}
What I need is for the JVM to consider the Foo instance from cl as if it is an instance of Foo from the classloader of the executing code.
I have seen these approaches, none of them good for me (the above example is a toy example):
- Load the class (or a separate interface) by a class loader that is a parent of both the calling code and created classloader
- Serialize and deserialize the object.
Solution 1:
Not possible. Class identity consists of the fully qualified name and the class loader.
Casting an object to a class with the same name loaded by different classloaders is no different than trying to cast a String
to Integer
, because those classes really could be completely different despite having the same name.
Solution 2:
I just spent the last two days struggling with this exact issue and I finally got around the problem by using java reflection:
// 'source' is from another classloader
final Object source = events[0].getSource();
if (source.getClass().getName().equals("org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptThread")) {
// I cannot cast to 'org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptThread'
// so I invoke the method 'terminate()' manually
Method method = source.getClass().getMethod("terminate", new Class[] {});
method.invoke(source, new Object[] {});
}
Hope this helps someone.
Solution 3:
If class which need be cast implements Serializable then:
private <T> T castObj(Object o) throws IOException, ClassNotFoundException {
if (o != null) {
ByteArrayOutputStream baous = new ByteArrayOutputStream();
{
ObjectOutputStream oos = new ObjectOutputStream(baous);
try {
oos.writeObject(o);
} finally {
try {
oos.close();
} catch (Exception e) {
}
}
}
byte[] bb = baous.toByteArray();
if (bb != null && bb.length > 0) {
ByteArrayInputStream bais = new ByteArrayInputStream(bb);
ObjectInputStream ois = new ObjectInputStream(bais);
T res = (T) ois.readObject();
return res;
}
}
return null;
}
usage:
Object o1; // MyObj from different class loader
MyObj o2 = castObj(o1);
Solution 4:
No possible to cast in different classLoader.
You have this workaround with Gson, example cast Object to YourObject (Object is a YourObject class but in other classLoader):
Object o = ...
Gson gson = new Gson();
YourObject yo = gson.fromJson(gson.toJson(o), YourObject.class);
I use this workaround because I compile any java code in a WebApp (on Tomcat). This workaround run in production.