Anyone know how to do this? I believe it is possible, but others say it isn't.
Foo foo = new Foo();
Object bar = BeanUtils.create("Bar");
BeanUtils.setProperty(bar, "foo", foo); // call bar.setFoo(foo)
// ... at this point, Bar.java changes and
// ... is recompiled (Commons JCI can do this)
bar = BeanUtils.create("Bar");
BeanUtils.setProperty(bar, "foo", foo); // breaks!!!
This last line is where everything starts to break down. Using commons JCI, I can get up to this line, but when Bar is reloaded, it thinks that Foo is a new class loaded by a different classloader. Setting the old Foo instance on it causes bad things to happen (ClassCastException, etc).
Couldn't this be done if each class had it's own ClassLoader? Is this just not possible in Java? A couple things to note:
- I specifically loaded Foo before Bar, so Foo does not get loaded as a dependency of Bar
- This code never references the Bar class directly (note the BeanUtils usage)
Update: I decided to put together a real Java program to show the issue.
public static void main(String[] args) throws Exception {
/*
Foo.java:
public class Foo {
static {
System.out.println("Foo initializing...");
}
}
Bar.java:
public class Bar {
static {
System.out.println("Bar initializing...");
}
private Foo foo;
public void setFoo(Foo foo) {
this.foo = foo;
}
}
*/
ClassLoader parent = Test.class.getClassLoader();
File base = new File("jci-test");
CompilingClassLoader ccl = new CompilingClassLoader(parent, base);
ccl.start();
Class fooClass = ccl.loadClass("Foo");
Object foo = fooClass.newInstance();
Class barClass = ccl.loadClass("Bar");
ClassLoader firstBarCL = barClass.getClassLoader();
ClassLoader fooCL = fooClass.getClassLoader();
System.out.println("firstBarCL == fooCL: " + (firstBarCL == fooCL));
Object bar = barClass.newInstance();
Method setter = barClass.getMethod("setFoo", new Class[]{foo.getClass()});
setter.invoke(bar, new Object[]{foo});
System.out.println("*************");
System.out.println("Updating Bar.java...");
new File(base, "Bar.java").setLastModified(System.currentTimeMillis());
Thread.sleep(1000 * 5);
System.out.println("*************");
barClass = ccl.loadClass("Bar");
ClassLoader secondBarCL = barClass.getClassLoader();
System.out.println("firstBarCL == secondBarCL: " + (firstBarCL == secondBarCL));
bar = barClass.newInstance();
setter = barClass.getMethod("setFoo", new Class[]{foo.getClass()});
setter.invoke(bar, new Object[]{foo});
/*
Output:
Foo initializing...
firstBarCL == fooCL: true
Bar initializing...
*************
Updating Bar.java...
*************
firstBarCL == secondBarCL: false
Bar initializing...
Exception in thread "main" java.lang.NoSuchMethodException: Bar.setFoo(Foo)
at java.lang.Class.getMethod(Class.java:1581)
at com.opensymphony.webwork.util.classloader.Test.main(Test.java:54)
*/
}