2014-03-31 7 views
8

Ich bin mit Nashorn über JSR 223 kleine Schnipsel von Benutzern eingegebenen Skript auszuführen:Java Scripting Mit Nashorn (JSR 223) & Pre-Compilation

public Invocable buildInvocable(String script) throws ScriptException { 
    ScriptEngine engine = new ScriptEngineManager().getEngineByName(ENGINE); 
    engine.eval(functions); 
    engine.eval(script); 
    return (Invocable) engine; 
} 

Der unterschiedliche Benutzer-Skript ruft JavaScript-Funktionen, die in einem definierten statische, zentrale Bibliothek (in der functions-Zeichenfolge im obigen Codefragment enthalten).

Jedes Mal, wenn ich eine Invocable erreichen möchte, die ich von meinem Java aufrufen kann, muss ich ständig den großen Bibliothekscode neu kompilieren.

Gibt es eine Möglichkeit, ein zuvor kompiliertes Stück Code mit neuem Code zu verbinden?

+1

Sind einige Sicherheitsprobleme gibt? z.B. inject malware codes – auntyellow

Antwort

10

Dies ist ein Design von JSR-223; eval(String) kann nicht wirklich einen Code-Cache hinter sich haben. Nun, theoretisch könnte es, aber es würde eine Menge Spekulation auf Seiten von dem, was der Entwickler will verkörpern (und wie alle Spekulationen, würde es zwangsläufig falsch sein).

Was Sie tun sollten, ist Ihre Invocable einmal bewerten, halten Sie es herum und verwenden Sie es wiederholt.

Beachten Sie, dass Nashorn keine Threadsicherheit bietet (JavaScript hat kein Threading-Konzept, daher ist Nashorn absichtlich nicht threadsicher, um Synchronisationskosten nicht bezahlen zu müssen, wenn sie nicht von der Sprachsemantik vorgegeben werden). Aus diesem Grund ist Ihre erstellte Invocable nicht sicher aus mehreren Threads in Bezug auf den Status der globalen Variablen im zugrunde liegenden Skript zu verwenden. (Gleichzeitig laufende Funktionen, die mit der globalen Skripts nicht Zustand ist in Ordnung interagieren.)

Wenn Sie es über Threads und die Funktionen abhängen teilen müssen globalen Zustand, und der globale Zustand ändern können, dann Sie müssen dafür Ihr eigenes Gerüst hinzufügen (entweder Synchronisierung oder Ressourcen-Pooling oder was auch immer gerade für diesen Zweck in Mode ist).

15

Put kompilierten Funktionen in Bindungen wie:

private static final String FUNCTIONS = 
    "function() {" + 
    " return \"Hello\";" + 
    "}"; 

public static void main(String... args) throws Exception { 
    ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript"); 

    // Compile common functions once 
    CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS); 
    Object sayHello = compiled.eval(); 

    // Load users' script each time 
    SimpleBindings global = new SimpleBindings(); 
    global.put("sayHello", sayHello); 
    String script = "sayHello()"; 
    System.out.println(engine.eval(script, global)); 
} 
+0

Gibt es eine Möglichkeit, ein Argument an das Skript weiterzugeben? –

+1

versuche 'String script =" sayHello (\ "Mein Argument \") ";' – auntyellow

0

Wenn Sie benötigen Sie vorkompilieren und JavaSctipt Funktionen mit verschiedenen Argumenten aufrufen Sie sie separat kompilieren kann und Ausführungsablauf in Java zubauen. Mit JavaScript-Engine Nashorn in Java8 können Sie tun:

private static final String FUNCTIONS = 
    "function hello(arg) {" +  //<-- passing java.lang.String from Java 
    " return 'Hello ' + arg;" +  //<-- returning string back 
    "};" + 
    "function sayTime(arg) {" + //<-- passing java.util.HashMap from Java 
    " return 'Java time ' + arg.get('time');" + //<-- returning string back 
    "};" + 
    "function() {" +     //<-- this callable "function pointer" is being returned on [step1] below 
    " return { 'hello': hello, 'sayTime': sayTime };" + 
    "};"; 

public static void main(String... args) throws Exception { 
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("Nashorn"); 

    CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS); 
    ScriptObjectMirror lastFunction = (ScriptObjectMirror)compiled.eval(); // [step1] 

    ScriptObjectMirror functionTable = (ScriptObjectMirror)lastFunction.call(null); // this method retrieves function table 
    String[] functionNames = functionTable.getOwnKeys(true); 
    System.out.println("Function names: " + Arrays.toString(functionNames)); 

    System.out.println(functionTable.callMember("hello", "Robert")); //<-- calling hello() with String as argiment 

    Map map = new HashMap(); 
    map.put("time", new Date().toString()); //<-- preparing hashmap 

    System.out.println(functionTable.callMember("sayTime", map)); //<-- calling sayTime() with HashMap as argument 
} 

Sie passieren können Java innerhalb von JavaSctipt Objekten, siehe Beispiel mit java.util.HashMap oben.

Ausgang ist:

Function names: [hello, sayTime] 
Hello Robert 
Java time Fri Jan 12 12:23:15 EST 2018