Link to home
Start Free TrialLog in
Avatar of msk_apk
msk_apkFlag for India

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.

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");
	}
}

Open in new window

Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

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
Avatar of msk_apk

ASKER

this for the sample code. i have tried that also. but i am getting the below exception.
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;
	}
}

Open in new window


E:\workspace\java\src>javac DynamicClassLoader.java

E:\workspace\java\src>java DynamicClassLoader
 printing here
 loading class ===PrintObj
Exception in thread "main" java.lang.IllegalAccessError: class PrintObj cannot access its superinter
face Print
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
        at MyClassLoader.findClass(DynamicClassLoader.java:49)
        at DynamicClassLoader.main(DynamicClassLoader.java:14)

E:\workspace\java\src>
ASKER CERTIFIED SOLUTION
Avatar of for_yan
for_yan
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

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();
    
}

Open in new window

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.  
Avatar of msk_apk

ASKER

having separate class files solved this problem.
Avatar of msk_apk

ASKER

good ideas
Avatar of msk_apk

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.
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.
 

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();

    }


    
}

Open in new window



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;
    }

}

Open in new window


package reflection;

public class MyObjectSuperClass {
}

Open in new window



package reflection;


public class MyObject extends MyObjectSuperClass {

    String s;

    public MyObject(){
        s = "MyClass-2";
    }

    public String toString(){
        return s;
    }

}

Open in new window



Output:

MyClass-2
Now replace class
MyClass-1

Open in new window

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.
Avatar of msk_apk

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/src/class/
[Loaded Print from file:/E:/workspace/java/src/class/]
[Loaded PrintObj from file:/E:/workspace/java/src/class/]
 printinting here
[Loaded MyClassLoader from file:/E:/workspace/java/src/class/]
 loading class ===PrintObj
[Loaded java.io.ByteArrayOutputStream 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]
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.