java


Java code cannot invoke method from scriptengine with new context


I am trying to implement example invoking method from Javascript in Java.
private static final String JS = "function doit(p) { list.add(p); return true; }";
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
List<String> list = new ArrayList<>();
ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine engine = scriptManager.getEngineByName("nashorn");
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
Bindings scope = context.getBindings(ScriptContext.ENGINE_SCOPE);
scope.put("list", list);
engine.eval(JS, context);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("doit", "Hello!!!");
System.out.println(list.size());
}
}
This code throws exception:
Exception in thread "main" java.lang.NoSuchMethodException: No such function doit
at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:204)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:383)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190)
at testjavascriptinteraction.TestJavaScript.main(TestJavaScript.java:32)
Version
java -version
openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-8u91-b14-3ubuntu1~16.04.1-b14)
OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)
What is wrong?
I can reproduce this problem, and have found a way how to solve it.
I had a hunch that after casting to Invocable, the engines own scripting context is still being used, and not the temporary one you have passed to eval.
I can fix this by calling engine.setContext(...) with this context before casting to Invacable. i.e.:
...
scope.put("list", list);
engine.eval(JS, context); // 'context' now contains the 'doit' function.
engine.setContext(context); // <-- Right here
Invocable invocable = (Invocable) engine; // Now the Invocable will use 'context'.
invocable.invokeFunction("doit", "Hello!!!"); //Runs fine
System.out.println(list.size());
The fact that this works seems to confirm my hunch.
Your scope and your eval() are both using a custom ScriptContext, but your invokeFunction() is not. The function is registered with the context, not the engine.
Option 1: Don't use a custom context.
List<String> list = new ArrayList<>();
ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine engine = scriptManager.getEngineByName("nashorn");
Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE);
scope.put("list", list);
engine.eval("function doit(p) { list.add(p); return true; }");
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("doit", "Hello!!!");
System.out.println(list); // prints: [Hello!!!]
UPDATE
Option 2: Call registered function directly.
A function get registered with the context as a normal variable with a function value. This means that your scope will have a doit variable after the eval() call.
You will have to use a Nashorn class to call it directly, but it's easy.
// same code as above up to eval call
engine.eval("function doit(p) { list.add(p); return true; }", context);
// Instead of invokeFunction(), just get and call function manually
JSObject func = (JSObject)scope.get("doit");
func.call(null, "Hello!!!"); // like a "static" call, no 'this' value
System.out.println(list); // prints: [Hello!!!]

Related Links

Android: calculate vehicle moving distance using mobile device
New Operator Internal memory allocation in heap [duplicate]
Loop through Object in ArrayList in java
get data onActivityResult from another application
How to merge two .apk files and make one .apk file? [duplicate]
Check Is the array is empty in one line
Swing splash screen not working, Unexpected result
cucumber step definitions are not being located
Big O: what is the time complexity for this algorithm?
Splitting a Decimal Value(as String ) using java
Dynamically set XML URI at runtime - XSLT option
Extract text from html file using java
Multiple servlet mappings in Spring Boot
Cannon Find Symbols Error
View.Listener is not abstract and does not override abstract method actionPerformed(ActionEvent) in ActionListener
XML parsing throwing NullPointerException

Categories

HOME
yii2
multithreading
gitlab
omnet++
server
heroku
google-play
iterator
stock
relay
xmpp
framework7
react-redux
gps
umd
ojdbc
mouse
autotools
android-youtube-api
python-unittest
ssl-client-authentication
zend-framework3
task
try-catch
finite-automata
zapier
orleans
graphicsmagick
language-agnostic
tooltipster
ping
ejabberd-module
kendo-datasource
r-raster
bcd
siesta-swift
fog
environment-modules
catalog
file-format
serve
scaffold
hockeyapp
ansible-playbook
mapzen
ncalc
sql-server-agent
botbuilder
midl
qwt
taffy
fault
outlook-2013
paxos
powercli
media-player
pillow
memory-alignment
imanage
bind9
synchronous
url-pattern
paypal-express
bstr
actionbardrawertoggle
qgraphicsview
wapiti
maven-tomcat-plugin
ora-00900
computer-algebra-systems
tarjans-algorithm
cakephp-3.1
key-management
notify
markers
castle
responsive-slides
phpthumb
expected-exception
seaside
transcoding
html5-notifications
farseer
easy-install
flash-builder4.5
html4
chuck
tridion-worldserver
dbproviderfactories
regsvr32
file-comparison
paster
preference
mirah
anti-piracy

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App