There should have been quite a few cases where you wanted to run the JavaScript engine in the JavaVM and use JavaScript as the DSL, but the JavaScript engine Nashorn that comes standard with the JavaVM is already deprecated in Java11. , It is a source of anxiety for those who use it in production.
The reason Nashorn is deprecated is that it can't keep up with the latest ECMAScript specification.
Certainly there is a motivation to use modern ES, but if that becomes a shackle and the speed of evolution of the JVM itself is controlled, it is reasonable to decide to separate it from the pace of evolution of the JVM itself as deprecation. Target.
Then what should I do? The first option is to use the multilingual JVM GraalVM, which is being developed as the Alt JVM. GraalVM is mentioned in various places, so I won't touch it here.
In actual operation, it is not possible to change the VM and operate it, but it is somewhat disappointing that the Java program does not work unless it is a specific JVM. Let's try Rhino, the standard for pre-Nashorn JS on JVM.
https://github.com/mozilla/rhino
If you take a look at the Rhino repository for the first time in a while, you'll see that the latest 1.7.13 was released in September 2020 and is still well maintained.
Looking at the Compatibility Table with ES2015, it seems that it is quite difficult, but the level of compatibility is gradually improving, so it seems that we can expect it in the future.
Rhino 1.7.13 supports the ScriptEngine interface of the JVM. Indeed, Nashorn can now be replaced as is.
To use the ScriptEngine interface with Rhino, you need rhino-engine.jar in addition to the traditional rhino.jar.
Therefore, describe the following dependency in pom.xml. (For Maven)
<!-- https://mvnrepository.com/artifact/org.mozilla/rhino -->
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
<version>1.7.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mozilla/rhino-engine -->
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino-engine</artifactId>
<version>1.7.13</version>
</dependency>
The simplest way to execute JavaScript would be code like this: You can use it as it is just by specifying rhino
for the engine name.
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("rhino");
try {
scriptEngine.eval("function add(a, b) { return a + b }");
Number v = (Number) scriptEngine.eval("add(13, 17)");
System.out.println(v);
} catch (ScriptException e) {
e.printStackTrace();
}
However, in the case of Nashorn, the actual type of Number
in the return value of the above code was Double
, but in the case of Rhino, it has changed to Long
. There seems to be various differences in the details.
In order to use a Java object from within JavaScript, it is necessary to set the object in the scope of JavaScript and call it.
Map<String, Object> map = new HashMap<>();
map.put("a", 10);
map.put("b", 20);
map.put("console", new MyConsole());
SimpleBindings bindings = new SimpleBindings(map);
scriptEngine
.eval("c = a + b;"
+ "a += b;"
+ "console.log('a=' + a + ', b=' + b + ', c=' + c);",
bindings);
Here, MyConsole
is a class created by myself as follows.
public class MyConsole {
public void log(Object arg) {
System.out.println(String.valueOf(arg));
}
}
Result is
a=30, b=20, c=30
It was as expected.
As with Rhino prior to 1.7.12, it is possible to use Rhino raw without going through the ScriptEngine interface.
The code in this case is as follows.
Context context = new ContextFactory().enterContext();
Scriptable globalScope = context.initStandardObjects();
Script script = null;
try (Reader reader = new FileReader(jsSourceFile)) {
script = context.compileReader(reader, jsSourceFile.getName(), 1, null);
} catch (IOException | EvaluatorException e) {
e.printStackTrace(System.err);
System.exit(1);
}
ScriptableObject.putProperty(globalScope, "a", 10);
ScriptableObject.putProperty(globalScope, "b", 20);
ScriptableObject.putProperty(globalScope, "console", new MyConsole());
// run global scope context
script.exec(context, globalScope);
When I wrote a program that manipulates Java objects with some JavaScript, I encountered the following cases.
var javaObject = someObject.getValue(); //javaObject is java.lang.String
console.log(javaObject); // "OK"
if (javaObject === 'OK') { // false
//Not equated with JavaScript string literals
console.log('===');
} else if (javaObject == 'OK') { // false
// ==But no
console.log('==');
} else if ('OK'.equals(javaObject)) { // true
console.log('equals');
}
The result is equals
. Java strings and JavaScript strings are different.
This needs attention.
Addendum
context.getWrapFactory().setJavaPrimitiveWrap(false);
You can change the behavior around here by calling>.
Rhino is steadily evolving. Even in 2021, there is no worry about adopting JavaScript as the DSL in Java applications.
Recommended Posts