I am trying to execute Javascript code from Java. Javascript code uses jquery so I prepend the jquery.js before my code. But it throws following exception,
Exception in thread "main" javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "window" is not defined. (<Unknown source>#1) in <Unknown source> at line number 1
As I run this from the Java code, I understand that it does not have access to the window object so above exception. I found that EnvJs provides the implementation for the required environment so I tried to load that first by putting its content first while generating the script content to eval. But run into following exception,
Exception in thread "main" javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: TypeError: Cannot call property getCurrentContext in object [JavaPackage org.mozilla.javascript.Context]. It is not a function, it is "object". (<Unknown source>#1247) in <Unknown source> at line number 1247
Following is the code snippet,
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
String script = "Envjs code" + "jQuery code" + "my java script"; //code of envjs + jquery from the link provided at the end
engine.eval(script);
Invocable inv = (Invocable) engine;
inv.invokeFunction("myFunc", obj1, obj2);
I do not use any browser features so do not require object's like window. So ideally I do not want to load Envjs. Please let me know how to load jQuery code.
One more question - How to pass Json Object from Java code to Javascript function as parameter?
http://www.envjs.com/dist/env.rhino.1.2.js
http://code.jquery.com/jquery-1.9.0.min.js
It may be easier to do this with Rhino using the instructions from the Envjs Guide ( http://www.envjs.com/doc/guides#running-embed ).
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.tools.shell.Global;
import org.mozilla.javascript.tools.shell.Main;
...
Context cx = ContextFactory.getGlobal().enterContext();
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_5);
Global global = Main.getGlobal();
global.init(cx);
Main.processSource(cx, "path/to/your/EnvJSfile");
Main.processSource(cx, "path/to/your/JQueryJSfile");
cx.evaluateString(global, "your JavaScript", "JavaScript", 1, null);
don't known about Envjs, but why simulate a browser environment in java?
for the second question:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
Compilable compilable = (Compilable) engine;
Bindings bindings = engine.createBindings();
String script = "function add(op1,op2){return op1+op2} add(a, b)";
CompiledScript jsFunction = compilable.compile(script);
bindings.put("a", 1);bindings.put("b", 2); //put java object into js runtime environment
Object result = jsFunction.eval(bindings);
System.out.println(result);
you can put whatever object into the bindings, a map, a list, or a pojo.
Related
I'm using JDK8 Nashorn scripting engine. My script registers a callback at a Java class like this:
stream.create().input(".env/router-memory-list/router1").management().onChange(function(){
print("onChange: "+input.current());
});
The input referenced in the callback function is set from the Java thread before executing the callback and removed after (ctx.engineScope is the Binding object of the script):
ctx.engineScope.put("input", input);
input.current(message).executeCallback();
ctx.engineScope.remove("input");
It usually works fine but I'm occasionally getting this kind of error while executing the callback:
<eval>:15 ReferenceError: "input" is not defined
at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:319)
at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:291)
at jdk.nashorn.internal.objects.Global.__noSuchProperty__(Global.java:1432)
at jdk.nashorn.internal.scripts.Script$Recompilation$4$383$\^eval\_.L:13(<eval>:15)
at jdk.nashorn.javaadapters.java.lang.Runnable.run(Unknown Source)
at com.swiftmq.impl.streams.comp.io.ManagementInput.executeCallback(ManagementInput.java:105)
at com.swiftmq.impl.streams.processor.StreamProcessor.visit(StreamProcessor.java:92)
at com.swiftmq.impl.streams.processor.po.POMgmtMessage.accept(POMgmtMessage.java:28)
at com.swiftmq.tools.pipeline.PipelineQueue.process(PipelineQueue.java:35)
at com.swiftmq.tools.queue.SingleProcessorQueue.dequeue(SingleProcessorQueue.java:135)
at com.swiftmq.tools.pipeline.PipelineQueue$QueueProcessor.run(PipelineQueue.java:69)
at com.swiftmq.impl.threadpool.standard.PoolThread.run(Unknown Source)
I tried to surround the calling code with a synchronized in the hope it might be a threading issue but without success. Looking at the stack trace it might be a recompilation issue where objects that were dynamically set at the Binding are getting lost. Any ideas?
Edit - here is how I create the Binding for this script:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName((String) entity.getProperty("script-language").getValue());
if (engine == null)
throw new Exception("Engine for script-language '" + entity.getProperty("script-language").getValue() + "' not found!");
ScriptContext newContext = new SimpleScriptContext();
ctx.engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
I embed jruby script engine into my java program by using javax.script.ScriptEngineManager
I made some jruby code that end with do ~ end block,
after running all code, NullPointerException occured.
but code ends with any other statement, no exception occurs.
version : 1.7.19
Caused by: java.lang.NullPointerException
at org.jruby.embed.variable.Argv.updateARGV(Argv.java:169)
at org.jruby.embed.variable.Argv.retrieve(Argv.java:158)
at org.jruby.embed.variable.VariableInterceptor.retrieve(VariableInterceptor.java:154)
at org.jruby.embed.internal.BiVariableMap.retrieve(BiVariableMap.java:378)
at org.jruby.embed.internal.EmbedEvalUnitImpl.run(EmbedEvalUnitImpl.java:124)
in ARGV.java updateARGV
if (vars.containsKey((Object)name)) {
var = vars.getVariable((RubyObject)receiver.getRuntime().getTopSelf(), name);
var.setRubyObject(argv);
vars.getVariable returned null because of isReceiverIdentical return false
in BiVariableMap.java
if (var.isReceiverIdentical(receiver)) {
return var;
}
In isReceiverIdentical, this method just compare receiver with BiVariable's receiver usgin '=='.
Is this jruby bug? Or do I have to do something for this?
If you need more information about this problem, plz comment it!
I got ScriptEngine(engine) from ScriptEngineManager and set some java instance and method like this
engine.put("this", console);
engine.eval("$command = $this.java_method :command, [java.lang.String]");
here is my test ruby code. result and tab is java object
that has some method return String and list.
result = $command.call "something to pass"
puts result.getMessage
tabular = result.getData
tabular.each do |tab|
rows = tab.getRows
rows.each do |row|
puts row
end
puts tab.getColumnNames
end
I had created ruby type object in my java code by creating new Ruby object...
This causes checking fail in updateARGV because a receiver that register variable in BiVariableMap and another receiver that update variable are different.
So, I got a Ruby object from new ScriptingContainer(from it we can always get a same Ruby object if local context is singleton) and used it to create new ruby type object in my java code.
Reference: https://github.com/jruby/jruby/wiki/RedBridge#Singleton
The java documentation about the JavaScript ScriptEngine implementation says, that it is possible to set the system property "rhino.opt.level" if there is no security manager active. ("When security manager is not used, System property "rhino.opt.level" can be defined in the range [-1, 9]. By default, the value is set to -1 which means optimizer is disabled.", see http://docs.oracle.com/javase/7/docs/technotes/guides/scripting/programmer_guide/#jsengine)
My question now is, how this can be done. I tried setting it as an environment variable and in the code using
System.setProperty("rhino.opt.level", "9");
but it had no effect on the compiled scripts whatsoever. Is there a command line argument that needs to be passed to the jvm or something similar?
Edit: My test code:
String script = IOUtil.readTextFile("test.js", "UTF-8"); // reads the file's content
System.setProperty("rhino.opt.level", "9");
final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
Compilable compiler = (Compilable) scriptEngine;
CompiledScript cs = compiler.compile(script);
cs.eval();
I'm currently using the javax implementation of Rhino. By default Rhino uses a wrapper to return Java objects. Does Nashorn have similar behaviour or does it return JavaScript objects by default?
Thanks
Looks like it tries its best to return sensible objects. Using this code, then changing the XXX:
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("nashorn");
engine.eval("function test() { return XXX; };");
Object result = ((Invocable)engine).invokeFunction("test");
System.out.println(result.getClass().getName());
Yields:
return 'hello world' = java.lang.String
return 1 = java.lang.Integer
return { name: 'Hello' } = jdk.nashorn.api.scripting.ScriptObjectMirror
Looks like that, even though the Java objects can be used within the JS code, it still references Java Objects (although they show up as function objects so there must be a wrapper there), we can't treat them as Javascript objects:
//"import"
var StringTokenizer = java.util.StringTokenizer;
print(typeof StringTokenizer);
var st = new StringTokenizer("this is a test");
print(typeof st);
java.util.StringTokenizer.prototype.name = 'myST';
print(st.name);
And here's the result:
testObj.js:9 TypeError: Cannot set property "name" of undefined
Now Javascript objects will be loaded as "jdk.nashorn.internal.scripts.JO" instances.
*If you want to test the above code more easily, just create an alias for your JDK's jjs (Nashorn Interpreter), e.g., if you create a file called test.js, you can run the program with:
$ jjs test.js
Mac OS = alias jjs=’/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/jjs’
Windows = Define an environment variable called ‘JAVA8_HOME’ and point to your jdk8 folder, then you can invoke jjs by running this command:
> “%JAVA8_HOME%\jre\bin\jjs” test.js
Suppose I have a Javascript file
function js_main(args){
/* some code */
var x = api_method1(some_argument);
/* some code */
}
And I try to run it with javax.scripting the usual way
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.eval(...);
Now the I'd like to handle the call to api_method1 in Javascript with my Java class. I'd like to have some kind of mapping/binding of calls i.e. each time the script calls api_method1(arg) a method
public Object api_method1(Object arg){ ... }
(placed in the same class as the engine) would be called.
Can I achieve this?
use engine.createBindings() to make a Bindings object;
put an object exposing your method into the bindings with some name:
Bindings b = engine.createBindings();
b.put("api", yourApiObject);
engine.setBindings(b, ScriptContext.ENGINE_SCOPE);
Then in JavaScript there'll be a global "api" object you can call:
api.method1( "foo", 14, "whatever" );
The facility is easy to use, but be careful with what you pass back and forth; it doesn't do that much to convert JavaScript types to Java types.