Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I load java class from database?

Tags:

java

With the following source code:

package util.abc;
public class Test{
    public String out(){
        return "Hello World!";
    }
}

I can use:

Class c = Class.forName("util.abc.Test");

to instantiate this Class, but I must put this source file(Test.java) in the classpath /util/abc/

I want to dynamically load this class from the database (store the source code as string or binary)

Is this possible ?

like image 414
Koerr Avatar asked Mar 16 '11 18:03

Koerr


2 Answers

Assuming you already compiled the class, you can create a DatabaseClassLoader, which loads the class from a database.

public class DatabaseClassLoader extends ClassLoader {

    public DatabaseClassLoader(ClassLoader parent, ... /* connection to database */ ) {
       super(parent);
       // store the connection
    }

    public Class findClass(String name) {
       byte[] data = loadDataFromDatabase(name);
       return defineClass(name, data, 0, data.length);
    }
    private byte[] loadDataFromDatabase(String name) {
        // this is your job.
    }
}

If the database only contains the source code, you'll have to compile it first - look into the Java compiler API for how to do this without any files.

Pay attention, the class loaded this way will stay alive as long as the classloader is alive, so you'll need a new class loader to be able to reload the class in case of changes.

Also, if you want to interact with the class by other ways than reflection, you would better let it implement some interface (which itself is in your class path), and let the application class loader be the parent of your database class loader.

Ah, and how to load:

Class<?> c = Class.forName("util.abc.Test", myClassLoader);

or directly

Class<?> c = myClassLoader.loadClass("util.abc.Test");

Here is a method which creates objects of your interface (of any interface, in fact):

public <X> X getImplementingObject(Class<X> interfaceClass, String className)
   throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
    ClassLoader loader = new DatabaseClassLoader(interfaceClass.getClassLoader(), ...);
    Class<?> cl = loader.loadClass(className);
    Class<? extends X> c = cl.asSubclass(interfaceClass);
    return c.newInstance();
}

(It needs the class to have a no-argument constructor which does not throw any exceptions, of course (if it does, you'll get this exception thrown, too).

This creates a new ClassLoader for every such class, so they can only cooperate with each other by means of the interface (or reflection).

For on-the-fly compiling you should look at the Java Compiler API, as mentioned in the answer from dcn. But I think it would be better to do the compiling on the side which puts the classes into the database than the side who pulls them out.

like image 154
Paŭlo Ebermann Avatar answered Oct 06 '22 06:10

Paŭlo Ebermann


If you want to store the source code in you DB, you can use the Java 6 Compiler API to compile it at runtime. See here for an example.

In order to load classes at runtime, you can either use an URLClassLoader if you can specify the location of the bytecode with a URL, or use ClassLoader.defineClass and supply the bytecode as an array of bytes.

Either way you should pay attention, that in order to use your dynamically loaded class, it should implement an interface that is known at compile time.

like image 35
dcn Avatar answered Oct 06 '22 05:10

dcn