Dynamic Java Code Injection
In this post we’re going to look at how to dynamically load Java code into a running jvm. The code might be completely new or we might want to change the functionality of some existing code within our program.
(Before we start you might be wondering why on earth anyone might want to do this. The obvious example is for something like a rules engine. A rules engine would want to offer the ability for users to add or change rules without having to restart the system. You could do this by injecting DSL scripts as rules which would be called by your rules engine. The real problem with such an approach is that the DSL scripts would have to be interpreted making them exceedingly slow to run. Injecting actual Java code which can then be compiled and run in the same way as any other code in your program will be orders of magnitude more efficient.
At Chronicle we are using this very idea at the heart of our new microsecond micro-services/algo container).
The library we are going to use is the open source Chronicle library Java-Runtime-Compiler.
As you will see from the code below, the library is exceedingly simple to use – in fact it really only takes a couple of lines. Create a CachedCompiler and then call loadFromJava. (See the documentation here for the actual simplest use case.)
The program listed below does the following:
- Creates a thread which calls compute on a Strategy every second. The inputs to the Strategy are 10 and 20.
- Loads a strategy which add two numbers together
- Waits 3s
- Loads a strategy which deducts one number from the other
This is the full code listing:
package test; import net.openhft.compiler.CachedCompiler; /** * Loads the addingStrategy and then after 3s replaces it with the * subtractingStrategy. */ public class DynamicJavaClassLoading { private final static String className = "test.MyClass"; private final static String addingStrategy = "package test;\n" + "import test.DynamicJavaClassLoading.Strategy;\n" + "public class MyClass implements Strategy {\n" + " public int compute(int a, int b) {\n" + " return a+b;\n" + " }\n" + "}\n"; private final static String subtractingStrategy = "package test;\n" + "import test.DynamicJavaClassLoading.Strategy;\n" + "public class MyClass implements Strategy {\n" + " public int compute(int a, int b) {\n" + " return a-b;\n" + " }\n" + "}\n"; public static void main(String[] args) throws Exception { StrategyProxy strategy = new StrategyProxy(); //Thread calling the strategy once a second Thread t = new Thread(() -> { while (true) { System.out.println(strategy.compute(10,20)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); { ClassLoader cl = new ClassLoader() { }; CachedCompiler cc = new CachedCompiler(null, null); Class aClass = cc.loadFromJava(cl, className, addingStrategy); Strategy runner = (Strategy) aClass.newInstance(); strategy.setStratgey(runner); } Thread.sleep(3000); { ClassLoader cl = new ClassLoader() { }; CachedCompiler cc = new CachedCompiler(null, null); Class aClass = cc.loadFromJava(cl, className, subtractingStrategy); Strategy runner = (Strategy) aClass.newInstance(); strategy.setStratgey(runner); } } public interface Strategy{ int compute(int a, int b); } public static class StrategyProxy implements Strategy{ private volatile Strategy underlying; public void setStratgey(Strategy underlying){ this.underlying = underlying; } public int compute(int a, int b){ Strategy underlying = this.underlying; return underlying == null ? Integer.MIN_VALUE : underlying.compute(a, b); } } }
This is the output (comments in blue):
The strategy has not been loaded yet. underlying in the StrategyProxy is null so Integer.MIN_VALUE is returned -2 1 4 7 4 8 3 6 4 8 The adding strategy has been loaded 10+20=30 30 30 30 After 3s the subtracting strategy is loaded. It replaces the adding strategy. 10-20=-10 -10 -10 -10 -10 -10
Note that in the code we created a new ClassLoader and a new CachedCompiler each time we loaded the Strategy. The reason for this is that a ClassLoader can only have one instance of a particular class loaded at any one time.
If you were only using this library to load new code you would do it like this, without creating a ClassLoader (i.e. using the default ClassLoader) and using the CachedCompiler.
Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode);
Reference: | Dynamic Java Code Injection from our JCG partner Daniel Shaya at the Rational Java blog. |
Are there any security implication of using dynamic code compilation during runtime.
How do you store the code that needs to be injected at runtime.
Can you compare this solution to an OSGi-based one? How is compilation & loading on the fly better than OSGi’s dynamic service lifecycle or using a Nashorn-based JavaScript?