Dlouho jsem hledal, jak uzivatelum snadno umoznit psat pluginy do me aplikace. Hodne se pouziva VBA (Visual Basic for Applications), Lisp a Lua. Me je nejblizsi JavaScript, navic v nem hodne lidi umi.
Moje potreba byla jednoducha: predat JavaScriptu promenne z Pythonu/Javy, pomoci JS s nima neco udelat a pote vratit vysledek.
Takze napriklad predam pole namerenych teplot za posledni hodinu. Uzivateluv algoritmus je vyhodnoti a vrati, jestli zapnout/vypnout kotel.
Java ma podporu vybornou, zvlaste od Javy 8. V Pythonu je to horsi, ale take funkcni.
Python
Zde je funkcni ukazka:
# https://blog.sqreen.io/embedding-javascript-into-python/
# http://stackoverflow.com/a/366763/1974494
# http://stackoverflow.com/a/601168/1974494
import signal
from contextlib import contextmanager
from py_mini_racer import py_mini_racer
ctx = py_mini_racer.MiniRacer()
# ---------------------
print( ctx.eval('1+1') )
# ---------------------
my_script_1 = """
function maxNumber(arr) {
var largest = arr[0];
for (var i = 0; i < arr.length; i++) {
if (largest < arr[i] ) {
largest = arr[i];
}
}
return largest;
}
"""
ctx.eval(my_script_1)
arr = [3, 6, 2, 56, 32, 5, 89, 32]
max = ctx.call("maxNumber", arr)
print(max)
# ---------------------
my_script_2 = """
function sleep(delay) {
var start = new Date().getTime();
while (new Date().getTime() < start + (delay * 1000));
return ":-)";
}
"""
ctx.eval(my_script_2)
class TimeoutException(Exception): pass
@contextmanager
def time_limit(seconds):
def signal_handler(signum, frame):
raise TimeoutException("Timed out!")
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(seconds)
try:
yield
finally:
signal.alarm(0)
try:
with time_limit(5):
result = ctx.call("sleep", 3)
print(result)
except TimeoutException as msg:
print("Timed out!")
try:
with time_limit(5):
result = ctx.call("sleep", 7)
print(result)
except TimeoutException as msg:
print("Timed out!")
# ---------------------
Prvni JavaScript najde v Python poli cisel nejvesti a vrati ho.
Druhy priklad je na pluginy vhodnejsi, nastavi timeout pro JavaScriptovy kod. Pokud uzivatel svuj algoritmus napr. zacykli, ukonci ho.
Zdrojak je na pochopeni dostatecny. A tady je jeho vystup:
/usr/bin/python3.5 /home/martin/PycharmProjects/JavaScript/miniracer_test1.py 2 89 :-) Timed out! Process finished with exit code 0
Java
package test.test;
public class User {
private String name;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package test.test;
import javax.script.Bindings;
import javax.script.Compilable; // Require Java 8 !!!
import javax.script.CompiledScript; // Require Java 8 !!!
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
public class JavaScriptInJava {
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
/* ----------------------------------------------------------------------- */
// Put (string) param to JavaScript.
engine.put("testString", "Hello, world!");
engine.eval("print(testString)");
// Put (string) param to JavaScript.
/* ----------------------------------------------------------------------- */
// Find max number in int array with JavaScript:
int[] array = { 3, 6, 2, 56, 32, 5, 89, 32 };
engine.put("arr", array);
String script =
"var largest = arr[0]; "
+ "function maxNumber(arr) { "
+ " for (var i = 0; i < arr.length; i++) { "
+ " if (largest < arr[i] ) { "
+ " largest = arr[i]; "
+ " } "
+ " } "
+ " return largest; "
+ "} "
+ "maxNumber(arr); ";
Number result = (Number) engine.eval(script);
System.out.println("Max=" + result.longValue());
// Find max number in int array with JavaScript.
/* ----------------------------------------------------------------------- */
// Work with Java class from JavaScript:
String userScript =
"user1.setName(\"Test User\"); "
+ "print( user1.getName() ); ";
Bindings bindings = new SimpleBindings();
User u = new User();
bindings.put("user1", u);
engine.eval(userScript, bindings);
// Require Java 8 !!!
// Compile JavaScript (faster):
if (engine instanceof Compilable) {
System.out.println(" Compiling...");
Compilable compEngine = (Compilable) engine;
CompiledScript cs = compEngine.compile(userScript);
cs.eval(bindings);
} else {
engine.eval(userScript, bindings);
}
// Compile JavaScript (faster).
// Require Java 8 !!!
// Work with Java class from JavaScript.
/* ----------------------------------------------------------------------- */
// Use JavaScript function in Java code:
String math =
"function addition(a, b) { "
+ " return a+b; "
+ "} "
+ " "
+ "function substraction(a, b) { "
+ " return a-b; "
+ "} "
+ " ";
engine.eval(math);
Invocable inv = (Invocable) engine;
int a = 10;
int b = 5;
System.out.println("A=" + a + " B=" + b);
Object aPlusB = inv.invokeFunction("addition", a, b);
System.out.println("A+B = " + aPlusB);
Object[] inputParams = {
new Integer(10),
b
};
Object aMinusB = inv.invokeFunction("substraction", inputParams);
System.out.println("A-B = " + aMinusB);
int x = (Integer) aPlusB + 1;
System.out.println("aPlusB + 1 = " + x);
// Use JavaScript function in Java code.
/* ----------------------------------------------------------------------- */
}
}
Na ukazce je videt opet nalezeni nejvetsiho cisla. Mnohem zajimavejsi vsak je pristup k Java objektum z JavaScriptu – trida User.
Od Javy 8 je mozne JavaScript zkompilovat a tim ohromne urychlit jeho provadeni.
Jde pouze o ukazku, ze to jde 🙂