wtorek, 30 października 2012

Changing class during runtime in Java

Java class is consisting of  three elements:

package + class name + class loader instance

Using the same class loader to load the same class twice won't succeed.

Possible solution:
1. Write the interface for reloadable class (MyReloadableClass.interface)
2. Write the class that implements that interface (Changable.java)
3. Extend the default class loader class (MyClassLoader.java)
4. Write the method to verify the results (Runtime.java)

My implementation of class loader still needs some time to work on (hardcoded path, and class name), but it is proving that the code works fine.

com.interfaces.MyReloadableClass.interface
package com.interfaces;

public interface MyReloadableClass {
    public String getString();
}
com.oop.Changable.java
package com.oop;

import com.interfaces.MyReloadableClass;

public class Changable implements MyReloadableClass{
    public String getString() {
        return "test1";
    }
}
com.classLoaders.MyClassLoader
package com.classLoaders;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
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 (!"com.oop.Changable".equals(name))
            return super.loadClass(name);

        try {
            String url = "file:/home/khozzy/IdeaProjects/ReflectionProject/out/production/ReflectionProject/com/oop/Changable.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("com.oop.Changable",classData,0, classData.length);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
com.run.Runtime.java
package com.run;

import com.classLoaders.MyClassLoader;
import com.interfaces.MyReloadableClass;
import java.lang.reflect.InvocationTargetException;
import java.util.Scanner;

public class Runtime {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        MyReloadableClass changableObj;

        ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();

        try {
            changableObj = (MyReloadableClass) getReloadedObject(parentClassLoader,"com.oop.Changable");
            System.out.println(changableObj.getString());

            System.out.println("Change and recompile source file, than press enter...");
            scanner.nextLine();

            changableObj = (MyReloadableClass) getReloadedObject(parentClassLoader,"com.oop.Changable");
            System.out.println(changableObj.getString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Object getReloadedObject(ClassLoader parentClassLoader, String name) throws
            ClassNotFoundException,
            NoSuchMethodException,
            InvocationTargetException,
            IllegalAccessException,
            InstantiationException {

        return new MyClassLoader(parentClassLoader).loadClass(name).getConstructor().newInstance();
    }
}
Output:
test1
Change and recompile source file, than press enter...

(do some changes in Changes.java, recompile it than hit enter)
test2

Brak komentarzy:

Prześlij komentarz