Releases: eobermuhlner/java-scriptengine
Release 2.0.0
API changes
Backport to Java 8
The java-scriptengine
was backported to Java 8.
This has some impact on the Java Module (Jigsaw) support.
- The
module-info.class
was removed - The
Automatic-Module-Name: ch.obermuhlner.scriptengine.java
header in theMANFEST.MF
was added again
In Java 8 the special permission policy codeBase
protocol jrt:
is not supported.
Therefore special codeBase
for the permission policy
jrt:/ch.obermuhlner.scriptengine.java/memory-class
was removed
and the http://ch.obermuhlner/ch.obermuhlner.scriptengine.java/memory-class
was used instead.
Enhancements
No enhancements.
Bugfixes
No Bugfix changes.
Examples
Note: The example code is available on github, but not part of the
java-scriptengine
library.
No changes in the examples.
Release 1.1.0
API changes
No breaking API changes.
Enhancements
Control visible classes of script during execution (Isolation)
In previous releases the classloader of the caller was not visible
to the script during execution.
It was therefore not possible to use classes of the application
from inside the script.
It is now possible to control the isolation level of the script.
Isolation.CallerClassLoader
: the script can see the classes of the
calling applicationIsolation.IsolatedClassLoader
: the script can only see JDK classes
and classes declared inside the script
The default behaviour is Isolation.CallerClassLoader
.
Renamed to getCompiledClass()
and getCompiledInstance()
Renamed the following methods:
JavaCompiledScript.getInstanceClass()
togetCompiledClass()
JavaCompiledScript.getInstance()
togetCompiledInstance()
For backwards compatibility the old methods are still available
but marked as @Deprecated
.
They old methods will be removed with the next major release.
Preparation for Java Module (Jigsaw)
In preparation for future support of Java Modules (Jigsaw) the java-scriptengine
is now a Java module.
Important: If the application is a Java Module then the java.scriptengine
cannot see classes in the application
(in other words Isolation.CallerClassLoader
does not work correctly).
module ch.obermuhlner.scriptengine.java {
exports ch.obermuhlner.scriptengine.java;
exports ch.obermuhlner.scriptengine.java.constructor;
exports ch.obermuhlner.scriptengine.java.execution;
exports ch.obermuhlner.scriptengine.java.name;
exports ch.obermuhlner.scriptengine.java.util;
requires transitive java.scripting;
requires java.compiler;
}
The OSGi Export-Package
declaration in the MANIFEST.MF
exports the
same packages.
Special codeBase
for permission policy
The script classes are executed using a special codeBase
:
jrt:/ch.obermuhlner.scriptengine.java/memory-class
This allows to grant specific permissions to the script classes.
Here an example policy file:
// global permissions (for the application and on-the-fly compiled script classes)
grant {
permission java.io.FilePermission "<<ALL FILES>>", "read";
};
// permissions for the example application
grant codeBase "file:/C:/Users/obe/git/java-scriptengine/ch.obermuhlner.scriptengine.example/out/production/classes/" {
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessSystemModules";
permission java.lang.RuntimePermission "closeClassLoader";
permission java.lang.RuntimePermission "createClassLoader";
permission java.util.PropertyPermission "application.home", "read";
permission java.util.PropertyPermission "env.class.path", "read";
permission java.util.PropertyPermission "java.class.path", "read";
permission java.util.PropertyPermission "java.home", "read";
};
// permissions for the java-scriptengine
grant codeBase "file:/C:/Users/obe/git/java-scriptengine/ch.obermuhlner.scriptengine.java/out/production/classes/" {
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessSystemModules";
permission java.lang.RuntimePermission "closeClassLoader";
permission java.lang.RuntimePermission "createClassLoader";
permission java.util.PropertyPermission "application.home", "read";
permission java.util.PropertyPermission "env.class.path", "read";
permission java.util.PropertyPermission "java.class.path", "read";
permission java.util.PropertyPermission "java.home", "read";
permission java.lang.RuntimePermission "exitVM";
};
// permissions for on-the-fly compiled script classes (notice the special URL)
grant codeBase "jrt:/ch.obermuhlner.scriptengine.java/memory-class" {
permission java.lang.RuntimePermission "exitVM";
permission java.util.PropertyPermission "java.home", "read";
};
// permissions for the jdk.compiler module
grant codeBase "jrt:/jdk.compiler" {
permission java.lang.RuntimePermission "closeClassLoader";
permission java.lang.RuntimePermission "createClassLoader";
permission java.util.PropertyPermission "application.home", "read";
permission java.util.PropertyPermission "env.class.path", "read";
permission java.util.PropertyPermission "java.class.path", "read";
permission java.util.PropertyPermission "java.home", "read";
};
Added MethodExecutionStrategy.byMainMethod()
Added MethodExecutionStrategy.byMainMethod()
that will call the public static void main(String[] args)
with the specified arguments.
Bugfixes
Fix javadoc in MethodExecutionStrategy
byArgumentTypes()
and byMatchingArguments()
The javadoc for the methods
MethodExecutionStrategy.byArgumentTypes()
MethodExecutionStrategy.byMatchingArguments()
described the return value wrong.
Examples
Note: The example code is available on github, but not part of the
java-scriptengine
library.
All of the example code in this documentation is runnable
in the class ScriptEngineExample
.
Added ScriptEnginePerformance
An example class ScriptEnginePerformance
was added to measure the
performance of the JavaScriptEngine
for compilation and evaluation
of scripts.
Release 1.0.1
API changes
ConstructorStrategy
may return null
to run static
methods
The ConstructorStrategy
is now allowed to return a null
instance.
This indicates that a static method should be called in the
ExecutionStrategy
.
The DefaultExecutionStrategy
and MethodExecutionStrategy
support this behaviour.
Using a NullConstructorStrategy
is the most convenient way to do this.
Bugfixes
MethodExecutionStrategy.byMatchingArguments()
did not check methodName
MethodExecutionStrategy.byMatchingArguments() is supposed to search for
a method with the specified methodName, but the name was actually ignored.
Engine version
The JavaScriptEngineFactory.getEngineVersion()
of release 1.0.0
reported a wrong version "0.0.1".
The new release reports the now correct version "1.0.1".
Enhancements
Improved error messages for ambiguous constructors and methods
The error messages for ambiguous constructors and methods in the
ScriptException
s thrown by the DefaultConstructorStrategy
and the MethodExecutionStrategy
have been improved to list the
ambiguous constructors and methods.
Ambiguous constructors with matching arguments found:
public ch.obermuhlner.scriptengine.java.constructor.DefaultConstructorStrategyTest$TestConstructor(java.lang.String,java.lang.Long,int)
public ch.obermuhlner.scriptengine.java.constructor.DefaultConstructorStrategyTest$TestConstructor(java.lang.String,java.lang.String,int)
Ambiguous methods 'doSomething' with matching arguments found:
public java.lang.String ch.obermuhlner.scriptengine.java.execution.MethodExecutionStrategyTest$TestMethod.doSomething(java.lang.String,java.lang.Long,int)
public java.lang.String ch.obermuhlner.scriptengine.java.execution.MethodExecutionStrategyTest$TestMethod.doSomething(java.lang.String,java.lang.String,int)
Javadoc
Javadoc was written for all public classes.
Examples
Note: The example code is available on github, but not part of the
java-scriptengine
library.
All of the example code in this documentation is runnable
in the class ScriptEngineExample
.
Release 1.0.0
Release 1.0.0
The java-scriptengine
(not to be confused with a javascript
script engine)
compiles and executes Java
files at runtime.
The script source is a standard Java class that must follow these rules:
- public class
- constructor with no arguments (default constructor)
- Callable entry point. One of the following:
- class implements
Supplier
: theget()
method is called - class implements
Runnable
: therun()
method is called - class has exactly one
public
method with no arguments: call it
- class implements
The script class can be arbitrarily named and may be in a named package or the default package.
Note: The scanner that parses the script for package and class names is very simple.
Avoid confusing it with comments that contain the keywords package
or public class
or comments between the keywords and the package/class names.
Simple usage
The following code snippet shows a simple usage of the Java script engine:
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
Object result = engine.eval("" +
"public class Script {" +
" public String getMessage() {" +
" return \"Hello World\";" +
" } " +
"}");
System.out.println("Result: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
The console output shows the result of the only method in the Script
class.
Result: Hello World
Compiling
Calling ScriptEngine.eval()
multiple times is very efficient because
the same script has to be compiled every time.
The JavaScriptEngine
implements the Compilable
interface which
allows to compile the script once and run it multiple times.
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
Compilable compiler = (Compilable) engine;
CompiledScript compiledScript = compiler.compile("" +
"public class Script {" +
" private int counter = 1;" +
" public String getMessage() {" +
" return \"Hello World #\" + counter++;" +
" } " +
"}");
Object result1 = compiledScript.eval();
System.out.println("Result1: " + result1);
Object result2 = compiledScript.eval();
System.out.println("Result2: " + result2);
} catch (ScriptException e) {
e.printStackTrace();
}
The console output shows that the same instance was called
multiple times (without recompiling the script).
Result1: Hello World #1
Result2: Hello World #2
Bindings instance variables (fields)
You can read and write variables, both instance variables (fields) and static variables,
by using Bindings
in the script engine.
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
Compilable compiler = (Compilable) engine;
CompiledScript compiledScript = compiler.compile("" +
"public class Script {" +
" public static String message = \"Counting\";" +
" public int counter = 1;" +
" public String getMessage() {" +
" return message + \" #\" + counter++;" +
" } " +
"}");
{
Bindings bindings = engine.createBindings();
Object result = compiledScript.eval(bindings);
System.out.println("Result1: " + result);
System.out.println("Variable1 message: " + bindings.get("message"));
System.out.println("Variable1 counter: " + bindings.get("counter"));
}
{
Bindings bindings = engine.createBindings();
bindings.put("message", "Hello world");
Object result = compiledScript.eval(bindings);
System.out.println("Result2: " + result);
System.out.println("Variable2 message: " + bindings.get("message"));
System.out.println("Variable2 counter: " + bindings.get("counter"));
}
} catch (ScriptException e) {
e.printStackTrace();
}
The console output shows that bindings can read and write values
from both instance and static variables of your class.
Result1: Counting #1
Variable1 message: Counting
Variable1 counter: 2
Result2: Hello world #2
Variable2 message: Hello world
Variable2 counter: 3
Advanced features of JavaScriptEngine
The JavaScriptEngine
has an additional API to control
the execution of the script class.
Set NameStrategy
in JavaScriptEngine
You can specify the strategy to determine the name of the script class
from the script.
public interface NameStrategy {
String getFullName(String script) throws ScriptException;
}
The default implementation DefaultNameStrategy
uses a simple
(regular expression based) scanner
to find the package name and the class name in the script.
Alternatively the FixNameStrategy
allows to set an explicit
fully qualified class name.
Set ConstructorStrategy
in JavaScriptEngine
You can specify the strategy to construct an actual instance of
the script class.
public interface ConstructorStrategy {
Object construct(Class<?> clazz) throws ScriptException;
}
The default implementation DefaultConstructorStrategy
uses the no-argument default constructor.
Additional static constructor methods DefaultConstructorStrategy
allow to use a constructor with explicit arguments.
The following example uses the
convenience DefaultConstructorStrategy.byMatchingArguments()
to to determine a matching constructor
using the given arguments:
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
JavaScriptEngine javaScriptEngine = (JavaScriptEngine) engine;
javaScriptEngine.setConstructorStrategy(DefaultConstructorStrategy.byMatchingArguments("Hello", 42));
Object result = engine.eval("" +
"public class Script {" +
" private final String message;" +
" private final int value;" +
" public Script(String message, int value) {" +
" this.message = message;" +
" this.value = value;" +
" }" +
" public String getMessage() {" +
" return \"Message: \" + message + value;" +
" }" +
"}");
System.out.println("Result: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
Set ExecutionStrategyFactory
in JavaScriptEngine
You can specify the strategy to execute the script class instance
by providing a factory that creates an ExecutionStrategy
from a Class<?>
.
public interface ExecutionStrategyFactory {
public ExecutionStrategy create(Class<?> clazz) throws ScriptException;
}
public interface ExecutionStrategy {
Object execute(Object instance) throws ScriptException;
}
The default implementation DefaultExecutionStrategy
supports the following:
- class implements
Supplier
: theget()
method is called - class implements
Runnable
: therun()
method is called - class has exactly one
public
method with no arguments: call it
Alternatively the MethodExecutionStrategy
can be used to call a specific method with its arguments.
Use one of the following static constructor methods:
MethodExecutionStrategy.byMethod(Method method, Object... arguments)
MethodExecutionStrategy.byMatchingArguments(Class<?> clazz, String methodName, Object... arguments) throws ScriptException
MethodExecutionStrategy.byArgumentTypes(Class<?> clazz, String methodName, Class<?>[] argumentTypes, Object... arguments) throws ScriptException
The MethodExecutionStrategy.byMatchingArguments()
is probably
the most convenient way. It determines a matching function
by name and the given arguments (ignoring null
arguments).
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
JavaScriptEngine javaScriptEngine = (JavaScriptEngine) engine;
javaScriptEngine.setExecutionStrategyFactory((clazz) -> {
return MethodExecutionStrategy.byMatchingArguments(
clazz,
"getMessage",
"Hello", 42);
});
Object result = engine.eval("" +
"public class Script {" +
" public String getMessage(Object message, int value) {" +
" return \"Message: \" + message + value;" +
" } " +
"}");
System.out.println("Result: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
The console output shows that getMessage("Hello", 42)
was called.
Result: Message: Hello42
Set ExecutionStrategy
in JavaCompiledScript
If you compile the script with Compilable
you can
specify the ExecutionStrategy
directly on the compiled script
instead of using the ExecutionStrategyFactory
on the engine.
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("java");
JavaScriptEngine javaScriptEngine = (JavaScriptEngine) engine;
javaScriptEngine.setExecutionStrategyFactory((clazz) -> {
return MethodExecutionStrategy.byMatchingArguments(
clazz,
"getMessage",
"Hello", 42);
});
JavaCompiledScript compiledScript = javaScriptEngine.compile("" +
"public class Script {" +
" public String getMessage(Object...
scriptengine-jshell 0.1.0
First release of the JShell script engine.