msk_apk
asked on
Java Class loading problem
I am trying to reload a class dynamically as per the below code.
1. After the first print, I am modifying the code and recompiling the code with different print statments. say for example the new printSomething method prints "here" alone.
2. after the second loadClass() statement, i am expecting the new printSomething() method to get executed which does not happen. It always prints "printing here" irrespective of how many times I load the class through loadClass() method.
Believe I am missing something here.
1. After the first print, I am modifying the code and recompiling the code with different print statments. say for example the new printSomething method prints "here" alone.
2. after the second loadClass() statement, i am expecting the new printSomething() method to get executed which does not happen. It always prints "printing here" irrespective of how many times I load the class through loadClass() method.
Believe I am missing something here.
import java.io.*;
import java.net.*;
public class DClassLoader {
public static void main(String a[]) throws Exception
{
ClassLoader sysLoader = DClassLoader.class.getClassLoader();
Print l1 = (Print)sysLoader.loadClass("PrintObj").newInstance();
l1.printSomething();
Thread.sleep(30000);
Print l2 = (Print)sysLoader.loadClass("PrintObj").newInstance();
l2.printSomething();
}
}
interface Print
{
public void printSomething();
}
class PrintObj implements Print
{
public void printSomething()
{
System.out.println(" printing here");
}
}
Are you restarting the vm after recompiling?
You need to write your own ClassLoader to be able to reload class.
This is an example. Perhaps it works.
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
This is an example. Perhaps it works.
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
ASKER
this for the sample code. i have tried that also. but i am getting the below exception.
E:\workspace\java\src>java c DynamicClassLoader.java
E:\workspace\java\src>java DynamicClassLoader
printing here
loading class ===PrintObj
Exception in thread "main" java.lang.IllegalAccessErr or: class PrintObj cannot access its superinter
face Print
at java.lang.ClassLoader.defi neClass1(N ative Method)
at java.lang.ClassLoader.defi neClassCon d(ClassLoa der.java:6 32)
at java.lang.ClassLoader.defi neClass(Cl assLoader. java:616)
at java.lang.ClassLoader.defi neClass(Cl assLoader. java:466)
at MyClassLoader.findClass(Dy namicClass Loader.jav a:49)
at DynamicClassLoader.main(Dy namicClass Loader.jav a:14)
E:\workspace\java\src>
import java.io.*;
import java.net.*;
public class DynamicClassLoader {
public static void main(String a[]) throws Exception
{
ClassLoader sysLoader = DynamicClassLoader.class.getClassLoader();
Print l1 = (Print)sysLoader.loadClass("PrintObj").newInstance();
l1.printSomething();
Thread.sleep(30000);
MyClassLoader loader = new MyClassLoader();
Print l2 = (Print)loader.findClass("PrintObj").newInstance();
l2.printSomething();
}
}
interface Print
{
public void printSomething();
}
class PrintObj implements Print
{
public void printSomething()
{
System.out.println(" printing here");
}
}
class MyClassLoader extends ClassLoader
{
public Class findClass(String name)
{
try
{
System.out.println(" loading class ==="+name);
String classname = name+".class";
FileInputStream stream = new FileInputStream(classname);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int value = -1;
while( (value = stream.read()) != -1)
{
outputStream.write(value);
}
byte[] array = outputStream.toByteArray();
return defineClass(name,array,0,array.length);
}
catch(Exception ee)
{
ee.printStackTrace();
}
return null;
}
}
E:\workspace\java\src>java
E:\workspace\java\src>java
printing here
loading class ===PrintObj
Exception in thread "main" java.lang.IllegalAccessErr
face Print
at java.lang.ClassLoader.defi
at java.lang.ClassLoader.defi
at java.lang.ClassLoader.defi
at java.lang.ClassLoader.defi
at MyClassLoader.findClass(Dy
at DynamicClassLoader.main(Dy
E:\workspace\java\src>
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Look how they are replacing the class in their example (from the link above):
One thing as I understand they load this class with their
special classloader in the first place, then create new ClassLoaded and load new version.
I'd suggest to try to follow their example closely at least for the first try.
public static void main(String[] args) throws
ClassNotFoundException,
IllegalAccessException,
InstantiationException {
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass("reflection.MyObject");
AnInterface2 object1 =
(AnInterface2) myObjectClass.newInstance();
MyObjectSuperClass object2 =
(MyObjectSuperClass) myObjectClass.newInstance();
//create new class loader so classes can be reloaded.
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass("reflection.MyObject");
object1 = (AnInterface2) myObjectClass.newInstance();
object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}
Luckily they even show example with omplementing interface, but I'd still
rather start without interface. When we see it works, we can always add it.
rather start without interface. When we see it works, we can always add it.
ASKER
having separate class files solved this problem.
ASKER
good ideas
ASKER
but still i am puzzled. Why i am getting IllegalAccessError when PrintObj and Print is part of the DynamicClassLoader.java file? all are have package specific access. there is no illegal access.
Yes I am modifiying the class during runtime but that class is getting loaded successfully if i restart the java program which means class is not corrupted. Hence some problem with the existing system class loader.
Yes I am modifiying the class during runtime but that class is getting loaded successfully if i restart the java program which means class is not corrupted. Hence some problem with the existing system class loader.
Great!
In the meantime. I also got interested and tried the code from
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
and after some pains (as always :) got it working like shown below.
After compiling MyObject class in two variants (with different strings)
I placed it in a separate location (specified in MyClassLoader of course)
and then replaced one version by another when
prompted. The expected output can be seen below.
In the procees I also realized the significance of having superclass or interface
to specify the type of the instance after we load the class.
Output:
In the meantime. I also got interested and tried the code from
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
and after some pains (as always :) got it working like shown below.
After compiling MyObject class in two variants (with different strings)
I placed it in a separate location (specified in MyClassLoader of course)
and then replaced one version by another when
prompted. The expected output can be seen below.
In the procees I also realized the significance of having superclass or interface
to specify the type of the instance after we load the class.
import reflection.MyObjectSuperClass;
public class LoadingClassOnTheFly {
public static void main(String[] args) throws
ClassNotFoundException,
IllegalAccessException,
InstantiationException {
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass("reflection.MyObject");
MyObjectSuperClass m1 = ( MyObjectSuperClass) myObjectClass.newInstance();
System.out.println(m1);
System.out.println("Now replace class");
// AnInterface2 object1 =
// (AnInterface2) myObjectClass.newInstance();
try{
Thread.currentThread().sleep(60000);
} catch (Exception ex){
System.out.println(ex.toString());
}
MyObjectSuperClass object2 =
(MyObjectSuperClass) myObjectClass.newInstance();
//create new class loader so classes can be reloaded.
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass("reflection.MyObject");
MyObjectSuperClass m2 = ( MyObjectSuperClass) myObjectClass.newInstance();
System.out.println(m2);
// object1 = (AnInterface2) myObjectClass.newInstance();
object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MyClassLoader extends ClassLoader{
public MyClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if(!"reflection.MyObject".equals(name))
return super.loadClass(name);
try {
String url = "file:C:/temp/test/reflection/MyObject.class";
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass("reflection.MyObject",
classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
package reflection;
public class MyObjectSuperClass {
}
package reflection;
public class MyObject extends MyObjectSuperClass {
String s;
public MyObject(){
s = "MyClass-2";
}
public String toString(){
return s;
}
}
Output:
MyClass-2
Now replace class
MyClass-1
I guess, you still need special custom ClassLoader which extends system class loader,
but is still different. Another issue perhpas has to to do with superclass.
As I was trying to do it, I first didn't pay attention to the superclass which in their example
also plays importnat role, and I could not overcome it without it - my guess is that when
it was loading MyObject with custom ClassLoader it wanted also to load its superclass Object,
but superclass was already loaded by the different class loader, which was causing
an error something like Circularity error (it is written in description that in normal
java programming you would hardly be able to create this error - still I saw it :).
Indeed, loks like these are rather subtle issues.
but is still different. Another issue perhpas has to to do with superclass.
As I was trying to do it, I first didn't pay attention to the superclass which in their example
also plays importnat role, and I could not overcome it without it - my guess is that when
it was loading MyObject with custom ClassLoader it wanted also to load its superclass Object,
but superclass was already loaded by the different class loader, which was causing
an error something like Circularity error (it is written in description that in normal
java programming you would hardly be able to create this error - still I saw it :).
Indeed, loks like these are rather subtle issues.
ASKER
if thats the reason, it should throw the same error when the class files are stored in separate class flies. the problem is it throws error when all classes are stored in the same file. but when they are stored in separate files, it loads the class with out any problem. in fact when the class files are stored in different files, it clearly loads the class from __JVM_DefineClass__. enabled java -verbose:class and got the below output.
[Loaded DynamicClassLoader from file:/E:/workspace/java/sr c/class/
[Loaded Print from file:/E:/workspace/java/sr c/class/]
[Loaded PrintObj from file:/E:/workspace/java/sr c/class/]
printinting here
[Loaded MyClassLoader from file:/E:/workspace/java/sr c/class/]
loading class ===PrintObj
[Loaded java.io.ByteArrayOutputStr eam from shared objects file]
[Loaded PrintObj from __JVM_DefineClass__]
printinting here
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]
[Loaded DynamicClassLoader from file:/E:/workspace/java/sr
[Loaded Print from file:/E:/workspace/java/sr
[Loaded PrintObj from file:/E:/workspace/java/sr
printinting here
[Loaded MyClassLoader from file:/E:/workspace/java/sr
loading class ===PrintObj
[Loaded java.io.ByteArrayOutputStr
[Loaded PrintObj from __JVM_DefineClass__]
printinting here
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]
Perhaps you are right; maybe what I observed was because at that moment I did not completely remove this MyObject
class from the project path, as I did later, and it was trying to load it both with system class loader and with custom class loader.
It didn't occur to me to check it with verbose.
Stiill, the Circularity error for some reason referred to Object, not to MyObject class.
It also looks that you cannot load this way just class without having superclass or interface to specify the type.
That's what kind of seems a little unsatisfying.
class from the project path, as I did later, and it was trying to load it both with system class loader and with custom class loader.
It didn't occur to me to check it with verbose.
Stiill, the Circularity error for some reason referred to Object, not to MyObject class.
It also looks that you cannot load this way just class without having superclass or interface to specify the type.
That's what kind of seems a little unsatisfying.