ExceptionInInitializerError when adding bindings to engine scope - javascript

Upgrading from nashorn to graalvm with openjdk 17. getting a PolyglotException: java.lang.ExceptionInInitializerError when adding bindings to engine scope. my script engine is getting initialized in init method & later used in different class. Wondering if its multi threading issue. unable to figure out the root cause
script engine gets initialized in init block of class 1 & 2.
Class 1
public void init() {
createScriptEngine
}
Class 2
public static ScriptEngine createScriptEngine (){
ScriptEngineManager manager = new ScriptEngineManager();
ret = manager.getEngineByName(engineName);
if(ret instanceof GraalJSScriptEngine) {
Bindings bindings = ret.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.nashorn-compat", true);
}
}
The engine itself is used in class 3 to evaluate script
class 3
private object evaluateScript(ScriptEngine scriptEngine, String script){
Bindings bindings = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put(SCRIPT_VAR_t1, this);
bindings.put(SCRIPT_VAR_t2, cTag);
bindings.put(SCRIPT_VAR_t3, tAttribs
Object ret = scriptEngine.eval(script, scriptEngine.getContext());
}

The issue is resolved now after creating context only once.

Related

using Javascript to set Java fields

Setting variables directly in Java classes doesn't seem to be working. Why not? What is the proper syntax? Where does the variable go??
The following prints out 2 and 1. Thus the f.x=2; never happened according to the object f of Foo.
#Test
public void testJS2Java() throws IOException, ScriptException, Exception {
ScriptEngineManager factory = new ScriptEngineManager();// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
class Foo {
int x = 1;
}
Foo f = new Foo();
engine.put("f", f);
System.out.println(engine.eval("f.x=2;"));
System.out.println(f.x);
}
The f.x=2; executes without error but which x was set?
Three issues with your test:
Nashorn allows access only to public members of public classes (from
exported modules for jdk9+) only. The local class Foo is
not public. So its members are not accessible from JavaScript.
Nashorn allows access to static members only from "Java type
objects" and not from instances of Java types. (different from Java).
Nashorn would ignore property sets on Java object if no public
field or public bean property with appropriate setter is found.
A working sample demonstrating access to a static Java field from Nashorn:
import javax.script.*;
public class Main {
public static int x = 10;
public static void main(String[] args) throws Exception {
ScriptEngine e = new ScriptEngineManager().
getEngineByName("JavaScript");
// access type object for Java class "Main" using Java.type
e.eval("var M = Java.type('Main');");
// access public static field 'x' of Main class
e.eval("print(M.x)");
// assign to public static field 'x' of Main class
e.eval("M.x += 10;");
// change is seen from Java
System.out.println(Main.x);
}
}

How to expose JS patch function of Incremental DOM library in GWT app using #JsInterop

I would like to use Incremental DOM library in my GWT app.
https://google.github.io/incremental-dom/#about
As I am coming from the Java world, I struggle with concepts of JavaScript namespaces and modules. I was able to use Closure Compiler with closure version of Incremental DOM (has to be build from sources).
It starts with the following line:
goog.module('incrementaldom');
So if I was to use it in regular JS I would type:
var patch = goog.require('incrementaldom').patch;
And then the patch function would be available in the scope of my code. But how to make it accessible from #JsInterop annotated classes?
I tried something like:
public class IncrementalDom {
#JsMethod(namespace = "incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
But it doesn't work. I get this error in the runtime:
(TypeError) : Cannot read property 'patch' of undefined
So I guess I have to somehow expose the incrementaldom module or at least only the patch method. But I don't know how.
After fighting for the whole day I found the solution. In the goog.module: an ES6 module like alternative to goog.provide document I found the missing information about the role of goog.scope function - required modules are visible only within the scoped call.
I created another Closure JS file named incrementaldom.js:
goog.provide('app.incrementaldom'); // assures creation of namespace
goog.require("incrementaldom");
goog.scope(function() {
var module = goog.module.get("incrementaldom");
var ns = app.incrementaldom;
app.incrementaldom.patch = module.patch;
});
goog.exportSymbol("app.incrementaldom", app.incrementaldom);
And now I can call it from Java code like this:
public class IncrementalDom {
#JsMethod(namespace = "app.incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
Still I have to define every object exported in original module separately in the Closure JS file. Fortunately I only need patch method. I hope one day I will find less cumbersome way for #JsInterop with goog.module :(

Javascript bridge / upcall to JavaFX (via JSObject.setMember() method) breaks when distributing

The Problem
I spent several hours trying to determine why my distributed code fails and yet my source code when debugging with the IDE (NetBeans) works without issue. I have found a solution and am posting to help others that might have similar issues.
BTW: I'm a self-taught programmer and might be missing a few fundamental concepts -- feel free to educate me.
Background Information
Using a WebView control within JavaFX application I load a webpage from an html file. I want to use JavaScript to handle the HTML side of things but I also need to freely pass information between Java and JavaScript (both directions). Works great to use the WebEngine.executeScript() method for Java initiated transfers and to use JSObject.setMember() in Java to set up a way for JavaScript to initiate information transfer to Java.
Setting up the link (this way breaks later):
/*Simple example class that gives method for
JavaScript to send text to Java debugger console*/
public static class JavaLink {
public void showMsg(String msg) {
System.out.println(msg);
}
}
...
/*This can be added in the initialize() method of
the FXML controller with a reference to the WebEngine*/
public void initialize(URL url, ResourceBundle rb) {
webE = webView.getEngine();
//Retrieve a reference to the JavaScript window object
JSObject jsObj = (JSObject)webE.executeScript("window");
jsObj.setMember("javaLink", new JavaLink());
/*Now in our JavaScript code we can type javaLink.showMsg("Hello!");
which will send 'Hello!' to the debugger console*/
}
The code above will work great until distributing it and attempting to run the JAR file. After hours of searching and testing different tweaks I finally narrowed the problem down to the JavaLink object itself (I eventually learned that you can use try-catch blocks in JavaScript which enabled me to catch the error: "TypeError: showMsg is not a function...").
The Solution
I found that declaring a global variable to hold an instance of the JavaLink class and passing that as a parameter into the setMember() method fixes it so that the app now runs both in the IDE as well as a standalone JAR:
JavaLink jl;
...
jl = new JavaLink();
//replace anonymous instantiation of JavaLink with global variable
jsObj.setMember("javaLink", jl);
Why!?
I'm guessing this has to do with garbage collection and that the JVM does not keep a reference to JavaLink unless you force it to by declaring a global variable. Any other ideas?
Just to demo that the hypothesis postulated in #DatuPuti's answer appears to be correct. Here's a quick test. Pressing the HTML button increments the counter in the HTML page, and also outputs to the system console. After forcing garbage collection by pressing the "GC" button, the updates to the system console stop:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class WebViewCallbackGCTest extends Application {
private final String HTML =
"<html>"
+ " <head>"
+ " <script>"
+ " var count = 0 ;"
+ " function doCallback() {"
+ " count++ ;"
+ " javaLink.showMsg('Hello world '+count);"
+ " document.getElementById('test').innerHTML='test '+count;"
+ " }"
+ " </script>"
+ " </head>"
+ " <body>"
+ " <div>"
+ " <button onclick='doCallback()'>Call Java</button>"
+ " </div>"
+ " <div id='test'></div>"
+ " </body>"
+ "</html>" ;
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
webView.getEngine().loadContent(HTML);
JSObject js = (JSObject) webView.getEngine().executeScript("window");
js.setMember("javaLink", new JavaLink());
Button gc = new Button("GC");
gc.setOnAction(e -> System.gc());
BorderPane root = new BorderPane(webView);
BorderPane.setAlignment(gc, Pos.CENTER);
BorderPane.setMargin(gc, new Insets(5));
root.setBottom(gc);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static class JavaLink {
public void showMsg(String msg) {
System.out.println(msg);
}
}
public static void main(String[] args) {
launch(args);
}
}
Of course, as stated in the other answer, if you declare an instance variable
private JavaLink jl = new JavaLink();
and replace
js.setMember("javaLink", new JavaLink());
with
js.setMember("javaLink", jl);
the problem is fixed, and the updates continue to appear in the console after calling System.gc();.
The solution from #DatuPuti works fine but partially. In my case, I've got multiple nested classes in the JavaLink class. Before doing what #DatuPuti exposes all the nested classes link broke and after applying the exposed solution, only the class being called is the one that keeps the link, all the rest are still breaking. This is the model of my JavaLink class:
public class JavaLink{
public NestedClass1 ClassName1;
public NestedClass2 ClassName2;
//Constructor
public JavaLink(){
ClassName1 = new NestedClass1();
ClassName2 = new NestedClass2();
}
//Nested classes being called from Javascript
public class NestedClass1(){}
public class NestedClass2(){}
}
The link would be created like this:
JavaLink javaLink;
...
javaLink = new JavaLink();
jsObject.setMember("JavaLink", javaLink);
Then, from Javascript I call classes methods like this:
JavaLink.ClassName1.method();
JavaLink.ClassName2.method();
And here comes the probem: When the crash occurs calling ClassName1 methods, ClassName2 unlinks and it's not available anymore using Javascript. The same happens if I the crash occurs while calling ClassName2 methods.
The solution that works for me (in addition to the exposed solution):
Besides declaring JavaLink in the higher scope possible, declaring all the nested classes will make them keep the link. For example (keeping the same reference from the example
class model):
JavaLink javaLink;
JavaLink.NestedClass1 nestedClass1;
JavaLink.NestedClass2 nestedClass2;
javaLink = new JavaLink();
nestedClass1 = javaLink.new NestedClass1();
nestedClass2 = javaLink.new NestedClass2();
//Creating an Object array to store classes instances
Object[] javaLinkClasses = new Object[2];
javaLinkClasses[0] = nestedClass1;
javaLinkClasses[1] = nestedClass2;
jsObject.setMember("JavaLink", javaLinkClasses); //Setting member as an array
And then finally, in order to call methods from nested classes in Javascript, object reallocation is needed, just like this (Javascript):
JavaLink.NestedClass1 = JavaLink[0];
JavaLink.NestedClass2 = JavaLink[1];
//Now we are able to call again methods from nested classes without them unlinking
JavaLink.NestedClass1.method();
I hope this helps people facing the same issue. I'm using Java JDK 1.8 with IntelliJIDEA.

Executing a function in a specific context in Nashorn

I have a custom Nashorn runtime that I set up with some global functions and objects - some of these are stateless and some of these are stateful. Against this runtime, I am running some custom scripts.
For each execution, I am planning on creating a new context that is backed by the global context:
myContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(myScript, myContext);
Based on what I read, any modifications to the global scope (from the perspective of the script) will be limited to the new context I created.
These scripts, when evaluated, expose some objects (with well-defined names and method-names). I can invoke a method on the object by casting engine to Invocable. But how do I know the context in which the function will run? Is that even an issue, or is the execution context of that function set up based on the context in which it was evaluated?
What behavior can I expect in a multithreaded situation where all threads share the same script-engine instance, and they all try to run the same script (which exposes a global object). When I then invoke the method on the object, in which context will the function run? How will it know which instance of the object to to use?
I was expecting to see an invoke method where I can specify the context, but this doesn't seem to be the case. Is there a way to do this, or am I going about this completely wrong?
I know that an easy way to get around this is to create a new script-engine instance per execution, but as I understand, I would lose optimizations (especially on the shared code). That being said, would pre-compiling help here?
I figured this out. The problem I was running into was that invokeFunction would throw a NoSuchMethodException because the functions exposed by the custom script didn't exist in the bindings from the engine's default scope:
ScriptContext context = new SimpleScriptContext();
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(customScriptSource, context);
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown
So what I had to do was pull out the function from the context by name and call it explicitly like so:
JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
function.call(null, args); //call to JSObject#isFunction omitted brevity
This will call the function that exists in your newly-created context. You can also invoke methods on objects this way:
JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
JSObject method = (JSObject) object.getMember(name);
method.call(object, args);
call throws an unchecked exception (either Throwable wrapped in a RuntimeException or NashornException that has been initialized with JavaScript stackframe information) so you may have to explicitly handle that if you want to provide useful feedback.
This way threads can't step over each other because there is a separate context per thread. I was also able to share custom runtime-code between the threads and ensure that state changes to mutable-objects exposed by the custom-runtime were isolated by context.
To do this, I create a CompiledScript instance that contains a compiled representation of my custom runtime-library:
public class Runtime {
private ScriptEngine engine;
private CompiledScript compiledRuntime;
public Runtime() {
engine = new NashornScriptEngineFactory().getScriptEngine("-strict");
String source = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/runtime.js")
).useDelimiter("\\Z").next();
try {
compiledRuntime = ((Compilable) engine).compile(source);
} catch(ScriptException e) {
...
}
}
...
}
Then when I need to execute a script I evaluate the compiled source, and then evaluate the script against that context as well:
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
//Exception handling omitted for brevity
//Evaluate the compiled runtime in our new context
compiledRuntime.eval(context);
//Evaluate the source in the same context
engine.eval(source, context);
//Call a function
JSObject jsObject = (JSObject) context.getAttribute(function, ScriptContext.ENGINE_SCOPE);
jsObject.call(null, args);
I tested this out with multiple threads and I was able to make sure that state changes were limited to the contexts that belong to individual threads. This is because the compiled representation is executed within a specific context, which means that instances of anything exposed by it are scoped to that context.
One small disadvantage here is that you may be needlessly reevaluating object definitions for objects that don't need to have thread-specific state. To get around this, evaluate them on the engine directly, which will add bindings for those objects to the engine's ENGINE_SCOPE:
public Runtime() {
...
String shared = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/shared.js")
).useDelimiter("\\Z").next();
try {
...
nashorn.eval(shared);
...
} catch(ScriptException e) {
...
}
}
Then later, you can populate the thread-specific context from the engine's ENGINE_SCOPE:
context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
One thing you will need to do is make sure that any such objects that you expose, have been frozen. Otherwise it is possible to redefine or add properties to them.

How To Initialize Nashorn Context and engine directly?

I am trying to initialize Oracle's javascript nashorn engine directy from jdk.nashorn.* namespace.
(nashorn library is a beta version of 2013 Jan).
There is a web sample which calles Nashorn engine instance of engine, using javax.script.ScriptEngineManager utility class.
var engine = ScriptEngineManager.getEngineByName(*)
However, I like to keep away from ScriptEngineManager, so i need to call engine directly in the same way Rhino can.
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
How can I create nashorn engine instance directly?
javax script engine by type application/javascript Hashorn, get back a script engine and tell it to do stuff, it also provides invokable and compilable interfaces.
Yout might be interested to read this : How can I start coding with Oracle's Nashorn JS Engine and when will it replace Rhino in the OpenJDK?
Example usage:
import javax.*; //lib imports
// we should use the javax.script API for Nahsorn
ScriptEngineManager m = new ScripteEngineManager();
ScriptEngine e = m.getEngineByname("nashorn");
try {
e.eval("print('hello nashorn !')");
} catch(Exception e) {
// using jdk lower then version 8 maybe ?
}
I found the way to init engine directly using .NET without "
"javax.script.ScriptEngineManager"
Environment:
IKVM.NET Version 8 + .NET Framework 4.52
static void Main(string[] args)
{
jdk.nashorn.api.scripting.NashornScriptEngineFactory fact = new jdk.nashorn.api.scripting.NashornScriptEngineFactory();
jdk.nashorn.api.scripting.NashornScriptEngine nashornengine = fact.getScriptEngine() as jdk.nashorn.api.scripting.NashornScriptEngine;
nashornengine.eval("var x = 1/3;");
object result = nashornengine.get("x");
Console.WriteLine("{0}", result);
}
This allows me directly interact with nashorn context methods mot more directly.
compile()
getFactory()
invokeMethod()
invokeFunction()

Categories