Does Nashorn return native JavaScript objects? - java

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

Related

Run python script using Pyrolite from java

I have a simple python script in my local machine, which returns a string. I want to run this script from java application and get the return value. I'm trying to do this using Pyrolite. I downloaded the jar files and added them to my java class path. But I'm not able to run the script.
I got the below sample code from readme.txt
NameServerProxy ns = NameServerProxy.locateNS(null);
PyroProxy remoteobject = new PyroProxy(ns.lookup("Your.Pyro.Object"));
Object result = remoteobject.call("pythonmethod", 42, "hello", new int[]{1,2,3});
String message = (String)result; // cast to the type that 'pythonmethod' returns
System.out.println("result message="+message);
remoteobject.close();
ns.close();
But this is not working for me. My system configuration is
OS: Windows 8
JDK: jdk1.7.0_51
Python: 2.6
Please help me with this.
This is how I have edited the code:
NameServerProxy ns = NameServerProxy.locateNS(null);
PyroProxy remoteobject = new PyroProxy();
Object result = remoteobject.call("C:\\trail1.py", null);
String message = (String)result; // cast to the type that 'pythonmethod' returns
System.out.println("result message="+message);
remoteobject.close();
ns.close();
I'm not positive that I understand what you're trying to do.
If you carefully read the tutorial, you'll see that you can't use Pyrolite the way you are. It specifies that you must have a python script running as a server, WITH a name server, where you must define some classes (for example Your.Pyro.Object).
Then you'll be able to call those objects you defined in that python script, but not the script itself.
To do what you want to do you'll need to call a function like C's fork(). Then you're able to call an executable, and you don't need Pyrolite.

Nashorn access non-static Java method

In Java 7 (1.7), I could access a Java method from JavaScript by running this:
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("importClass(net.apocalypselabs.symat.Functions);");
jse.eval("SyMAT_Functions = new net.apocalypselabs.symat.Functions();");
String input = "notify(\"Foo\");"; // This is user input
jse.eval("with(SyMAT_Functions){ "+input+" }");
Which would run the notify() function from the Functions java class:
public class Functions {
private Object someObjectThatCannotBeStatic;
public void notify(Object message) {
JOptionPane.showMessageDialog(null, message.toString());
}
/* Lots more functions in here, several working with the same non-static variable */
}
How do I access the Functions class in Java 1.8 with the Nashorn engine? My goal is to run different code for the first snippet if the user has Java 1.8, while still allowing people with 1.7 to use the app.
I've tried http://www.doublecloud.org/2014/04/java-8-new-features-nashorn-javascript-engine/ , https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html , and How to instantiate a Java class in JavaScript using Nashorn? without luck. None of them seem to allow me the same thing as Java 1.7 did, instead assuming I only want to access static functions and objects.
The most common error I get:
I start with...
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("var SyMAT_Functions;with (new JavaImporter(Packages.net.apocalypselabs.symat)) {"
+ "SyMAT_Functions = new Functions();}");
...then...
jse.eval("with(SyMAT_Functions){ "+input+" }");
...spits out...
TypeError: Cannot apply "with" to non script object in <eval> at line number 1
I was able to reproduce. First of all, Nashorn doesn't try to make it difficult to use Java objects (non-static or otherwise) in general. I have used it in other projects and not had any major issue converting from Rhino in Java 7 beyond what is covered in the migration guide. However, the issue here appears to deal with the use of the with statement which is "not recommended" and is even disallowed in strict mode of ECMAScript 5.1, both according to MDN.
Meanwhile, I found a thread on the Nashorn-dev mailing list discussing a similar case. The relevant part of the response was:
Nashorn allows only script objects (i.e., objects created by a JS
constructor or JS object literal expression) as scope expression for
"with" statement. Arbitrary objects . . . can not be used as 'scope' expression for
'with'.
In jdk9, support has been added to support script objects mirror other
script engines or other globals (which are instances of ScriptObjectMirror).
It's not the most elegant solution but, without using JDK 9, I was able to get your intended use of with to function by writing a proxy object inside the Javascript to mirror the public API of the Java class:
package com.example;
import javax.script.*;
public class StackOverflow27120811
{
public static void main(String... args) throws Exception {
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval(
"var real = new Packages.com.example.StackOverflow27120811(); " +
"var proxy = { doSomething: function(str) { return real.doSomething(str); } }; "
);
jse.eval("with (proxy) { doSomething(\"hello, world\"); } ");
}
public void doSomething(String foo) {
System.out.println(foo);
}
}
Attila Szegedi pointed out the non-standard Nashorn Object.bindProperties function. While it can't be expected to work with anything but the Nashorn engine, it does eliminate the complexity of re-declaring all of the public API inside the proxy object. Using this approach, the first jse.eval(...) call can be replaced by:
jse.eval(
"var real = new Packages.com.example.StackOverflow27120811(); " +
"var proxy = { }; " +
"Object.bindProperties(proxy, real); " // Nashorn-only feature
);
I decided to compile and bundle the "old" Rhino interpreter with my application instead of using Nashorn.
https://wiki.openjdk.java.net/display/Nashorn/Using+Rhino+JSR-223+engine+with+JDK8

How do I evaluate a Groovy string within a script?

I'm essentially trying to create a CLI with Groovy. I have a whole JavaFX GUI set up in Java and I want to be able to type in groovy script to run different functions inside a groovy script.
For example, say I have this script:
void meow() {
println "walrus"
}
I want to be able to type in "meow();" and press enter and evaluate it using the script as a reference.
I've tried using
shell.evaluate(inputStr, "src/Server/Scripting/CommandLineScript.groovy");
but to no avail; it just comes up with the error:
groovy.lang.MissingMethodException: No signature of method: CommandLineScript.meow() is applicable for argument types: () values: []
I can call other standard functions, such as:
shell.evaluate("println 'Hello World!';");
but I just can't run my own methods... How to solve it?
The following worked for me.
evaluate(new File("/Users/jellin/meow.groovy"))
I did change the meow.groovy file to execute the method within the file.
void meow() {
println "walrus"
}
meow()
One issue is I don't see a way to pass a parameter to the calling script.
I have used the following before, you can pass parameters as part of the binding.
String script = "full path to the script"
GroovyScriptEngine gse = new GroovyScriptEngine()
Binding binding = new Binding();
Object result = gse.run(script, binding)
Also, you might be able to simply reference the other scripts as classes and execute the run method on them.
There is also an AST transformation that can be used to have scripts extend a base script.
See here for more info
http://mrhaki.blogspot.com/2014/05/groovy-goodness-basescript-with.html
Thanks for your time guys; after a little more searching (always after posting a question do I find the answer in research >,<), I found that you can set a base class for the GroovyShell... I did it this way:
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
loader.addClasspath("src/ScriptLoc/");
binding = new Binding();
CompilerConfiguration compConfig = new CompilerConfiguration();
compConfig.setScriptBaseClass("ScriptName");
shell = new GroovyShell(loader, binding, compConfig);
I thought there would be a way to do it, and there it is... Now whenever I need to evaluate a script from the text box, I can just evaluate it and it evaluates it in the context of the base script.

Call javascript(jQuery/Envjs) from java code

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.

Bind method call in JavaScript script in Java Scripting

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.

Categories