So, I don't really know how to formulate this correctly but I'll do my best.
I am writing a game engine (not really for anything, I am trying a lot of ways to get certain things working to practice with ways of programming). And for this I want scripting. And I know how to add scripts from within Java but I have seen game engines that use multiple languages.
So what I want is to add python scripts that are run from with the Java process and can interact with Java object.
Like this Java Object that has some parameters (just an example)
public class Entity {
Script script = new Script ( "example.py" );
loc_x = 0;
loc_y = 0;
loc_z = 0;
public void update () {
script.run ();
}
}
With the python script being this
loc_x += 1
loc_z = loc_x
or
entity.loc_x += 1
entity.loc_z = entity.loc_x
I just have no way how to implement this. If this question has already been asked than please show me. If Runtime.getRuntime ().exec ( "example.py" ); is my best shot for this than that's fine. In that case I just want to know how to share those parameters.
Also, if another language (for example; LUA) is better for something like this then that's also fine. I am just completely blank on this subject.
So actually this is quite simple to do with Java having this built in from the box.
Java has this thing that is called 'ScriptEngineManager'. To use it you just do the following:
ScriptEngineManager sem = new ScriptEngineManager ();
ScriptEngine se = sem.getEngineByName ( "python" );
Now there are a few ways to runa script. Simple call the se.eval () method. You can give this either a String or a Reader and this way it will run the script.
Now to make it have some variables simply use the se.put method. You need to give this two parameters; a String and an Object.
For example:
se.put ( "entity", entity ); // with entity being defined earlier
The onlt thing to keep in mind is that this script manager does not have built in python support. You need to either create your own ScriptEngine for this or use third party software. I found jython and this seems to be working prety well. If you download the standalone jar and put that in your classpath it works. No need for any function calling.
Now in the Script you can call any public member of entity. All the objects, values and those sub-objects get passed through to the script.
My end-code:
Entity class
public class Entity {
String source =
"entity.loc_x += 1\n" +
"entity.loc_z = entity.loc_x";
ScriptEngine se;
loc_x = 0;
loc_y = 0;
loc_z = 0;
public Entity () {
ScriptEngineManager sem = new ScriptEngineManager ();
se = sem.getEngineByName ( "python" );
se.put ( "entity", this );
}
public void update () {
se.eval ( source );
}
}
I hope I helped anyone with this. It was pretty fun tinkering with all of this.
Related
I would like to get a clear answer on how to Sandbox execution Nashorn within a Java Application.
I have seen 'similar questions' (which I will refer to) but ultimately none of the answer seem to address my concerns.
Let me start with definitions.
Assume we start with this:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
engine.put("map",new HashMap());
engine.eval(jsCode); // jsCode can access 'map' only.
By "Sandboxing" I mean ensure that the JavaScript must not access any java object except the one added in the scope.
so the following evals should be fine.
engine.eval("map.toString()");
engine.eval("map.size()");
engine.eval("map.put('name','jeff'); ");
engine.eval("map.getClass()");
But the following evals will not:
engine.eval("var m = new java.util.HashMap();"); // <-- stop accessing Java
engine.eval("map.getClass().forName('java.io.File'); "); // stop. it's trying to be sneaky
Finally, I am not concerned about this:
engine.eval("while(1) {;}"); // this is impossible to detect. Maybe it's possible for this simple case... but sneaky users could make it impossible to detect... anyway this is not what I am asking. I am only concerned on accessing java objects.
So by sandboxing I intend to prevent jsCode to access java objects that I don't define.
I saw that this might be a potential solution:
jdk.nashorn.api.scripting.NashornScriptEngineFactory factory = new jdk.nashorn.api.scripting.NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(new jdk.nashorn.api.scripting.ClassFilter() {
public boolean exposeToScripts(String s) {
return false;
}
});
but is it 'safe' to access a package beginning with jdk.* directly ?
Another approach I saw is even more mysterious:
final ScriptEngine engine =
new NashornScriptEngineFactory().getScriptEngine(new String[] { "--no-java" });
I saw that one here:
Safely re-using sandboxed Nashorn containers
Can somebody let me know ?
You can use jdk.nashorn.api.scripting.* API if that would help in your application. javadoc for the same is here -> https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/
And yes, --no-java is the option for preventing java package access from script code.
My application uses a ScriptEngine to offer plugin-ability to my end-users.
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
Whenever a user makes changes to his script, then the application replaces the engine instane by a new instance.
String newScript = ...;
engine = engineManager.getEngineByName("nashorn");
engine.eval(newScript);
Two closely related questions:
Should I try to reuse engines and perform some kind of clear() on them ?
If I just replace my engine with a new instance, should I dispose the previous instance in some way, to avoid memory leaks ? (e.g. I can imagine that the user could manage to create a script that starts a thread.)
The problem is, I cannot find any method that looks like a clear() or a dispose(). Does that mean that my current approach is correct ?
You can use a single engine instance but use separate Bindings objects. Bindings acts as a top-level program environment, so if you want to evaluate a script into what is basically a "new global scope" then you could do that. Look into javax.script API docs on how to do this. You can either use ScriptEngine.eval that takes a Bindings as second argument or the one that takes ScriptContext as second argument.
Even if there's no script code surviving from the previous evaluation, you'll save some initialization time as the script engine will already have predefined various JavaScript data-holder classes and property maps ("hidden classes").
Also: yes, everything is garbage collected. There's no need for an explicit "disposal" API.
I just wanted to share what I tested myself. It makes perfect sense, but for those still in doubt: Created threads do continue to run if you just replace engine instances:
public static void main(String[] args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
String script =
"new java.lang.Thread(function() {\n" +
" for(;;) {" +
" print('Here\\'s Johnny !');" +
" java.lang.Thread.sleep(1000);" +
" }\n" +
"}).start();";
ScriptEngine engine = manager.getEngineByName("nashorn");
try {
engine.eval(script);
} catch (ScriptException e) {
e.printStackTrace();
}
// replace engine
engine = manager.getEngineByName("nashorn");
engine.eval("print('please, make it stop!!!');");
// please collect !!!
System.gc();
}
Output:
Here's Johnny !
please, make it stop!!!
Here's Johnny !
Here's Johnny !
Here's Johnny !
...
I guess that the garbage collector can clean the scripts, but not their actions outside their context. I think created threads are not even linked to the scripts in any way (i.e. outside their scope). So, I think it's just impossible for the jvm to detect or decide that these threads are linked to a replaced script and may or may not be stopped.
But this leads us too far for one stackoverflow question. Let's just focus on the ability to dispose/clear the bindings (i.e. ScriptContext).
Block java threads in nashorn scripts:
A possible solution, is to narrow down the available functionality. Here follow a couple of ways to avoid the creation of threads:
The following disables all java functionality:
// the option -nj is short for --no-java
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine("-nj");
But you can also disable specific classes, using a ClassFilter.
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine((className) -> {
if ("java.lang.Thread".equals(className)) return false;
if ("java.lang.Runnable".equals(className)) return false;
if ("java.util.Timer".equals(className)) return false;
if (className.startsWith("java.util.concurrency")) return false;
if (className.startsWith("javafx")) return false;
if (className.startsWith("javax.swing")) return false;
if (className.startsWith("java.awt")) return false;
return true;
});
Note: as soon as you define a ClassFilter also reflection classes are blocked automatically. So, you don't have to block those packages explicitly.
I am writing something that may be called workflow engine. For that I have created data model for the workflow as XML following specific XML Schema.
Below is an example of XML representing this data model:
<dm:agentModel xmlns:dm="ProcessObjects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="ProcessObjects agentModel.xsd">
<dm:cfp isList="false" objName="myCFP"></dm:cfp>
<dm:proposal isList="true" objName="receivedProposals"></dm:proposal>
<dm:feedbackList objName="cfpFeedbacks">
<dm:item>
<dm:to>Andrew</dm:to>
<dm:from>Paul</dm:from>
<dm:heading>That is bad</dm:heading>
<dm:body>Fix points a, b and c, please.</dm:body>
</dm:item>
<dm:item>
<dm:to>Frank</dm:to>
<dm:from>Paul</dm:from>
<dm:heading>Very good!</dm:heading>
<dm:body>I see no drawbacks. You can also ask Matthew for additional feedback.</dm:body>
</dm:item>
</dm:feedbackList>
</dm:agentModel>
The workflow definition, which is defined by the user by the means of web editor is the BPMN XML standard. For not going too deep in the details, i need to give user possibility to define some scripting interface. User needs to be able to writhe something like:
/*JavaScript code*/
for(var i=0; i<agentModel.cfpFeedbacks.length; i++) {
if(agentModel.cfpFeedbacks[i].to == "Frank") {
agentModel.cfpFeedbacks[i].to += " Sinatra";
}
}
By now, I wrote Java class (DataModel) that can access data built from XML given above. Because XML may contain many different objects, there are getters and setters that looks like:
/*Java code*/
DataModel agentModel = new DataModel(xmlString);
agentModel.getValue("cfpFeedbacks[1].to");
//returns String "Frank"
agentModel.setValue("cfpFeedbacks[0].from", "Paul Anka");
//obvious
To run user-written script I am trying to use Java Scripting API
/*Java code*/
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
//binding object
engine.put("agentModel", agentModel);
String script = "var i = 0;"
+ "println(agentModel.getValue(\"cfpFeedbacks[\" + i + \"].from\"));";
engine.eval(script);
Which is more or less working. What I want to archieve is something like this working:
/*Java code*/
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
//binding object
engine.put("agentModel", agentModel);
String script = "var i = 0;"
+ "println(agentModel.cfpFeedbacks[i].from);";
/**
* any magic operations here
*/
engine.eval(script);
Goal is to provide easiest-possible interface for end users to write their scripts.
I am a little bit lost and I would be grateful for any inspiration. Personally I did consider three scenarios:
Creating Java-Bean style classes, and compiling them instead of working on XML
Parsing script string from second to the first form (which seems to be most easy, but time expensive and definitely not 'clean')
Developing some kind of magic interface to the object where calling object.field is synonym for calling object.getValue("field")
Maybe there is some obvious workaround I don't see.
Thanks in advance for any replies,
PS. If my description is unclear, or you find it is worth to provide more source code I'll clarify question immidiately. Getting it done is right now priority for me.
The best way is deserialize xml to java object and then just put result into engine.
You are able to direct operate on objects putted via engine.put(..) method
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
engine.put("a", 1);
engine.put("b", 5);
engine.eval("a = 2;");
Object result = engine.eval("c = a + b;");
System.out.println("a + b = " + result);
Finally get object from script engine and serialize object to xml, jackson will be useful
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
Is it possible to use JavaScript in Android?? if so, how? Please provide some examples.
Thanks.
I am way late to the party here, but I had this exact need. iOS 7 now includes JavaScriptCore
natively and it is really easy to use (despite limited documentation). The problem is that
I didn't want to use it unless I could also use something similar on Android. So I created the AndroidJSCore project. It allows you to use your JavaScript code natively in Android without requiring a bulky WebView and injection. You can also seamlessly make asynchronous
calls between Java and Javascript.
Update 27 Mar 17: AndroidJSCore has been deprecated in favor of LiquidCore. LiquidCore is based on V8 rather than JavascriptCore, but works essentially same. See the documentation on using LiquidCore as a raw Javascript engine.
From the documentation:
... to get started, you need to create a JavaScript JSContext. The execution of JS code
occurs within this context, and separate contexts are isolated virtual machines which
do not interact with each other.
JSContext context = new JSContext();
This context is itself a JavaScript object. And as such, you can get and set its properties.
Since this is the global JavaScript object, these properties will be in the top-level
context for all subsequent code in the environment.
context.property("a", 5);
JSValue aValue = context.property("a");
double a = aValue.toNumber();
DecimalFormat df = new DecimalFormat(".#");
System.out.println(df.format(a)); // 5.0
You can also run JavaScript code in the context:
context.evaluateScript("a = 10");
JSValue newAValue = context.property("a");
System.out.println(df.format(newAValue.toNumber())); // 10.0
String script =
"function factorial(x) { var f = 1; for(; x > 1; x--) f *= x; return f; }\n" +
"var fact_a = factorial(a);\n";
context.evaluateScript(script);
JSValue fact_a = context.property("fact_a");
System.out.println(df.format(fact_a.toNumber())); // 3628800.0
You can also write functions in Java, but expose them to JavaScript:
JSFunction factorial = new JSFunction(context,"factorial") {
public Integer factorial(Integer x) {
int factorial = 1;
for (; x > 1; x--) {
factorial *= x;
}
return factorial;
}
};
This creates a JavaScript function that will call the Java method factorial when
called from JavaScript. It can then be passed to the JavaScript VM:
context.property("factorial", factorial);
context.evaluateScript("var f = factorial(10);")
JSValue f = context.property("f");
System.out.println(df.format(f.toNumber())); // 3628800.0
Do you mean something like making a native app using Javascript? I know there are tools like Titanium Mobile that let you make native apps using web tools/languages.
You could also make Web Apps. There are loads of resources and tutorials out there for that. Just search "Android Web App tutorial" or something similar.
Yes you can, just create a wrap up code that points to html page and includes your javascript and css.
There are different libraries that can help you with this:
http://www.phonegap.com/
http://www.sencha.com/products/touch/
http://jquerymobile.com/
Just posting this for posterity but React Native is a great solution in this space. You can write a complete app in JS using native views under the hood, or embed a JS component inside an existing Java app. https://reactnative.dev/