Overriding the Classloader is non-trivial, but fear not because there are open source libraries to accomplish this hotness for us. The one I will be using today can be found here. I'll present the code first and then we will go over a few points.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Message.java **/ | |
public interface Message { | |
public void sayIt(); | |
} | |
/** HelloWorld.java **/ | |
public class HelloWorld implements Message { | |
public void sayIt() { | |
System.out.println("Hello World"); | |
} | |
} | |
/** HelloHotswap.java **/ | |
public class HelloHotswap implements Message { | |
public void sayIt() { | |
System.out.println("Hello Hotswap"); | |
} | |
} | |
/** Main.java **/ | |
public class main { | |
public static void main(String[] args) { | |
String basepath = args[0]; | |
Example example = new Example(basepath); | |
String line = ""; | |
while(true) { | |
try { | |
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); | |
line = stdin.readLine(); | |
} | |
catch (java.io.IOException e) { System.out.println(e); } | |
catch (NumberFormatException e) { System.out.println(e); } | |
if(line.equals("speak")) { | |
example.speak(); | |
} else if(line.equals("exit")) { | |
System.exit(0); | |
} else { | |
example.changeImpl(line); | |
} | |
} | |
} | |
} | |
/** Example.java **/ | |
import org.codemonkey.javareflection.ExternalClassLoader; | |
public class Example { | |
Class impl; | |
ExternalClassLoader loader; | |
public Example(String basepath) { | |
loader = new ExternalClassLoader(); | |
loader.setBasepath(basepath); | |
} | |
public void changeImpl(String path) { | |
try { | |
impl = loader.loadClass(path); | |
} catch(ClassNotFoundException e) { | |
System.err.println("Class not found"); | |
} catch(Exception e) { | |
System.err.println("Something went wrong"); | |
} | |
} | |
public void speak() { | |
try { | |
Message instance = (Message)impl.newInstance(); | |
instance.sayIt(); | |
} catch(Exception e) { | |
System.err.println("Reflection error"); | |
} | |
} | |
} |
The interface Message and its two implementers are simple enough. It allows use to call a single method on each instance we load dynamically.
The main method will sit in a loop and wait for stdin input. When the main method is first invoked it takes the base path containing .java and .class files. This base path can even be dynamically changed with the library we are using, but I will avoid that for now. The main method will take input, inspect it, and call the corresponding method in Example.
Example is where we use the library to hot swap our Message Implementation. We hold one Class object named impl. Java Class objects hold information on a compiled class and allow us to make new instances of that compiled class. The Class object can hold any type of compiled Java class which is useful for the type of reflection we are going to do. The method changeImpl will look around from the basepath to try and find a match and will then change impl to hold the Class data for what it has found. The method speak uses the Class object to make a new instance and call the method Speak which the class has by implementing Message.
When we run this application we can change between HelloWorld and HelloHotswap and call speak accordingly. The code provided has no knowledge of HelloWorld and HelloHotswap, yet it can make use of it! In fact, if we wrote another Message implementation, compiled it and put it in the basepath's directory then we could load our new implementation without restarting the application. When we must have zero downtime this is a very powerful tool in the java tool belt.
Many open source libraries for dynamic loading exist and they can be useful for situations where we want to add functionality without shutting down our application. The ClassLoader and its underlying connection to the virtual machine allow these libraries to exist. By over riding the ClassLoader we can control the virtual machine's access to .class files. With this type of control we open the door to hot swapping at run time, which useful and a great dynamic-oriented addition to Java.
No comments:
Post a Comment