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
Related
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.
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.
So, I'll be using the Java Scripting API with JavaScript to do all the scripting for the game. Now, I've read over the documentation I can't seem to figure out how I could do a one time run of some of the scripts to get all the 'different types of objects data' to be fed to Java. I'm actually not quite sure how to save all that data to Java or if I should even try saving it to Java....
QUESTION: How can I import a bunch of scripting information at run-time into my application?
You can basically pass data between scripting environment and Java through the scripting API. For example,
final ScriptEngineManager factory = new ScriptEngineManager();
final ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval("greeting='Hello'");
// Returning data from scripting environment to Java.
// The data can also be returned from a function
final String greeting = (String) engine.eval("greeting");
System.out.println(greeting); //prints Hello
//Passing data to scripting environment from Java
engine.put("who", "foo");
final String greetingFoo = (String) engine.eval("greeting + ', ' + who");
System.out.println(greetingFoo); //prints Hello, foo
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.
How can I use java to get a js file located on a web server, then execute the function in the js file and get the result and use the result in java.
Can you guys give me some code snippet? Great thanks.
You can use the scripting engine built into Java:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public static void main(String[] args) throws Exception {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
Object result = engine.eval("my-java-script-code")
System.out.println("Result returned by Javascript is: " + result);
}
Here is a more elaborate example.
There's three steps to this process:
Fetch the JS file from the server.
Execute some JS function from the file.
Extract the result.
The first step is fairly simple, there are lots of HTTP libraries in Java that will do this - you effectively want to emulate the simple functionality of something like wget or curl. The exact manner in which you do this will vary depending on what format you want the JS file in for the next step, but the process to get hold of the byte stream is straightforward.
The second step will require executing the JS in a Javascript engine. Java itself cannot interpret Javascript, so you'd need to obtain an engine to run it in - Rhino is a common choice for this. Since you'd need to run this outside of Java, you'll likely have to spawn a process for execution in Rhino using ProcessBuilder. Additionally, depending on the format of the Javascript you might need to create your own "wrapper" javascript that functions like a main class in Java and calls the method in question.
Finally you need to get the result out - obviously you don't have direct access to JavaScript objects from your Java program. The easiest way is going to be for the JS program to print the result to standard out (possibly serialising as something like JSON depending on the complexity of the object), which is being streamed directly to your Java app due to the way you launched the Rhino process. This could be another job for your JS wrapper script, if any. Otherwise, if the JS function has observable side effects (creates a file/modifies a database) then you'll be able to query those directly from Java.
Job done.
I hope you realise this question is far too vague to get full answers. Asking the public to design an entire system goes beyond the point where you'll get useful, actionable responses.
There are plenty of examples on the web of how to download a file from a URL.
Suns version of the JDK and JRE includes the Mozilla Rhino scripting engine.
Assuming you have stored the contents of the javascript file in a string called 'script', you can execute scripts as follows
String result = null;
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
try {
jsEngine.eval(script);
result = jsEngine.get("result")
} catch (ScriptException ex) {
ex.printStackTrace();
}
The result will be extracted from the engine and stored in the 'result' variable.
The is a tutorial on scripting in Java that might be useful.