jruntime - java library that makes easier access to API of compiler.
This library let to us compile, and execute Java-code dynamically during working of the program. Per se it makes easier to use and extends capabilities provided by compilation subsystem API that was introduced in Java6. See following entry on our blog: [http://blogs.helion-prime.com/henadiyatroshko/2008/06/13/on-the-fly-compilation-in-java6.html]
Main functionality based on RuntimeClass class, and on helper that enlighten work with API of javac compiler. Let's start from example:
RuntimeClass myFirstRuntimeClass = new RuntimeClass(
"public class MyRuntimeClass{" +
"public String toString() {return \"MyRuntimeClass\";}" +
"}");
Object instance = myFirstRuntimeClass.newInstance();
System.out.println(instance);
As you see it is really straightforward; there are creation of dynamic class in the first line, then creation of instance of dynamic class, and then execution of method on newly created instance.
Another simple example of using of this library is creation and execution of anonymous code blocks:
new RuntimeBlock( "System.out.println(\"Hello from RuntimeBlock.\");").invoke();
Here you don't event need to describe the class, just describe code that should be executed. Blocks can receive and return parameters:
RuntimeBlock sum = new RuntimeBlock( "return (Integer)params[0]+(Integer)params[1];"); System.out.println(sum.invoke(5,4)); System.out.println(sum.invoke(100,100));
Parameters can be passed to the block of code via array Object params[], and using return you can return the result.
During using of library you will have cases when you need handle errors of code compilation or exceptions thrown by anonymous block.
If class description passed to class RuntimeClass contains errors then exception CompilationException will thrown. By handling of it you can get information about class name and error. Look on following example:
try {
RuntimeClass myFirstRuntimeClass = new RuntimeClass(
"public class MyRuntimeClass{\n" +
// "public String toString() {\n" +
// Lets replace String with void
"public void toString() {\n" +
" return \"MyRuntimeClass\";}" +
"}");
} catch (CompilationException e) {
System.out.println(
"Class " + e.getClassName() + " contains " +
e.getDiagnostics().size() + " error(s).");
for (Diagnostic d : e.getDiagnostics()) {
System.out.println(
"Error in line " + d.getLineNumber() + ":\n" + d);
}
}
As a result of execution you will get following:
> Class MyRuntimeClass contains 2 error(s). > Error in line 2: > runtime:///MyRuntimeClass.java:2: toString() in MyRuntimeClass cannot override > toString() in java.lang.Object; attempting to use incompatible return type > found : void > required: java.lang.String > Error in line 3: > runtime:///MyRuntimeClass.java:3: cannot return a value from method whose result > type is void
Class Diagnostic contains full information about error: string number, row, type, etc.
Anonymous dynamic code blocks can also throw exceptions; these exceptions are wrapped to InvocationTargetException.
Example:
try {
new RuntimeBlock(
"throw new Exception(\"This is test exception.\");").invoke();
} catch (InvocationTargetException e) {
System.out.println(e.getTargetException().getMessage());
}
As a result of execution you will get following:
> This is test exception.
The jruntime library allows to create instances of classes with possibility of dynamic extension of their functionality:
public class SomeClass {
public String someMethod(String someText) {
System.out.println("Original method code, text: " + someText);
return someText;
}
}
Library capability shows following example:
MutableRuntimeClass<SomeClass> mutable =
new MutableRuntimeClass<SomeClass>(SomeClass.class);
SomeClass instance = mutable.newInstance();
((Mutable)instance).setMutator(new SimpleMutator() {
@Override
public void onBeforeMethod(Object instance, Method method,
Object... params) {
System.out.println(
"Code before original method invocation.");
}
@Override
public Object onAfterMethod(Object instance, Method method,
Object result, Object... params) {
System.out.println(
"Code after original method invocation.");
return result;
}
});
instance.someMethod("Some text!!!");
An access to dynamic modification functionality provided by class MutableRuntimeClass. As parameter during instance contraction it receives class which will be accepted as base.
Further an instance of created class can be dynamically modified. This instance extends the class that specified as base class and by default it inherits Mutable interface that contains 2 methods: getMutator and setMutator.
The following code block creates anonymous mutator which output informational messages before and after calling of original method. Then using setMutator method mutator is delivered to class instance.
Last string execute method. The result of execution:
> Code before original method invocation. > Original method code, text: Some text!!! > Code after original method invocation.
The library let us completely change behavior of methods. Look on following examples that show it:
((Mutable) instance).setMutator(new SimpleMutator() {
@Override
public boolean hasMethodSubstitute(Object instance,
Method method, Object... params) {
if ("someMethod".equals(method.getName())) {
return true;
}
return false;
}
@Override
public Object onInsteadMethod(Object instance, Method method,
Object... params) {
if (hasMethodSubstitute(instance, method, params)) {
System.out.println("Original method code had to say: " +
params[0].toString() +
", but method substitute think: Hello!!!;)");
return params[0];
} else {
return super.onInsteadMethod(instance, method, params);
}
}
});
instance.someMethod("Some text!!!");
Here muttator implements 2 other methods. First method should returns “true” in case if method provided as parameter has substitute.
Second method of mutator directly implement new functionality.
Execution of last string of example give to us following result:
> Original method code had to say: Some text!!!, but method substitute think: Hello!!!;)
Using of method substitutes useful for example for access control.