(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:
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
The adding strategy has been loaded 10+20=30
After 3s the subtracting strategy is loaded. It replaces the adding strategy. 10-20=-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);