Java Class loading problem

msk_apk
msk_apk used Ask the Experts™
on
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

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2016

Commented:
Are you restarting the vm after recompiling?
Top Expert 2016

Commented:
Awarded 2011
Awarded 2011

Commented:
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
Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

Author

Commented:
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>
Awarded 2011
Awarded 2011
Commented:
Not obvious to me right away, but this experiment is challenging
by itself - what if you try it first, if it works at all
 without having interface - you probably do not necessarily need to have it
at least for the beginning.

Another option to try - compile interface as a separate file,
you don't need to change it to run the experiment.
Still, for the beggining I'd even remove oit altogether, just to see that this class loader works. Then we'll think of it aditionally.
 
Awarded 2011
Awarded 2011

Commented:

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

Awarded 2011
Awarded 2011

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

Author

Commented:
having separate class files solved this problem.

Author

Commented:
good ideas

Author

Commented:
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.
Awarded 2011
Awarded 2011

Commented:
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

Awarded 2011
Awarded 2011

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

Author

Commented:
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]
Awarded 2011
Awarded 2011

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



Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial