Rhino and concurrent access to javax.script.ScriptEngine

I'm using Rhino 1.6r2 through the javax.script API. I know that the Rhino engine claims to be MULTITHREADED: "The engine implementation is internally thread-safe and scripts may execute concurrently although effects of script execution on one thread may be visible to scripts on other threads."

What I'd like to know is, under what exact conditions would the effects of one script execution be visible to another? In my code, I sometimes re-use a ScriptEngine object, but for every execution I create a new SimpleBindings and pass it to eval(String, Bindings). With this arrangement, is there any way that internal state could leak from one execution to another? If so, how?

There's a very informative answer here, but it doesn't quite tell me what I need to know.

Yes, JSR223 didn't specify how variables in script language should be bound with given Bindings. Therefore it is totally possible the implementers choose storing global scope variables in engine instance and reuse it even given different Bindings when evaluating script.

For example, JRuby's JSR223 binding has one mode working in this way

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class Jsr223Binding {


    private Jsr223Binding() throws ScriptException {
        System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jruby");
        ScriptContext ctx1 = new SimpleScriptContext();
        ScriptContext ctx2 = new SimpleScriptContext();
        engine.eval("$foo = 5\nputs $foo", ctx1);
        engine.eval("puts $foo", ctx2);
    }

    public static void main(String[] args) throws ScriptException {
        new Jsr223Binding();
    }
}

Rhino scopes and contexts, Before using Rhino in a concurrent environment, it is important to the scripts are responsible for coordinating any accesses to shared variables. Note that currently in order to use Java classes (LiveConnect) from a sealed  Oracle's implementation of JDK 7 is co-bundled with the Mozilla Rhino based JavaScript engine which can be used in conjunction with javax.script (JSR-223) API. This directory and subdirectories contain the Rhino sources with Oracle changes. Mozilla Rhino sources are licensed under Mozilla Public License Version 1.1.

The javax.script package is thread-safe, but if your script isn't, you can have concurrency problems. The global variables inside the script is visible to all Threads. So, avoid using global variables inside your javascript functions

I'm running into this problem right now. My javascript is as follows:

function run(){
    regex = 0;
    regex += 1;
    return regex;
}

And I'm running it inside a ThreadPool(4) 10.000 times, and printing the result.

for (int i = 0; i <= 10000; i++){
        executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Double result = (Double) invocable.invokeFunction("run");
                    System.out.println(result);
                } catch (Exception e) {}
            }
        });
    }

This is a piece of the output:

1.0
2.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
2.0
1.0
1.0
0.0

Practical Nashorn, Part 2: The Java in JavaScript, The key element of the Java Scripting API is the ScriptEngine object, which binds Functions are accessed with the invoke family of methods, and entire scripts may be that were ported from Mozilla Rhino to Nashorn for backwards compatibility. requires the exact method signature executor["submit(java.util.​concurrent. 4 Rhino and concurrent access to javax.script.ScriptEngine Jul 2 '15 0 Spring - conventional way to create a @Bean before another @Bean loaded from the library Sep 26 '16 0 Jackson Conditional Validation of a field Sep 26 '16

I modified https://stackoverflow.com/a/1601465/22769 answer to show that rhino script engine execution is completeley thread safe if you specify the context in the eval() function. The sample calls fibonacci javascript function 100 times from 5 different threads concurrently:

package samplethread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class JSRunner {

    private static final ScriptEngine engine;
    private static final ScriptEngineManager manager;

    private static final String script = "function fibonacci(num){\r\n" + 
            "  var a = 1, b = 0, temp;\r\n" + 
            "\r\n" + 
            "  while (num >= 0){\r\n" + 
            "    temp = a;\r\n" + 
            "    a = a + b;\r\n" + 
            "    b = temp;\r\n" + 
            "    num--;\r\n" + 
            "  }\r\n" + 
            "\r\n" + 
            "  return b;\r\n" + 
            "} \r\n" + 
            "var out = java.lang.System.out;\n" + 
            "n = 1;" +
            "while( n <= 100 ) {" +
            "   out.println(java.lang.Thread.currentThread().getName() +':'+ 'FIB('+ n +') = ' + fibonacci(n));" +
            "   n++;" +
            "   if (java.lang.Thread.interrupted()) {" +
            "       out.println('JS: Interrupted::'+Date.now());" +
            "       break;" +
            "   }" +
            "}\n"; 

    static {
        manager = new ScriptEngineManager();
        engine = manager.getEngineByName("JavaScript");
    }

    public static void main(final String... args) throws Exception {
        for(int i = 0;i<5;i++) {
            try {
                final Bindings b = engine.createBindings();
                final SimpleScriptContext sc = new SimpleScriptContext();
                sc.setBindings(b, ScriptContext.ENGINE_SCOPE);
                execWithFuture(engine, script,sc);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void execWithFuture(final ScriptEngine engine, final String script,final ScriptContext sc) throws Exception {
        System.out.println("Java: Submitting script eval to thread pool...");
        ExecutorService single = Executors.newSingleThreadExecutor();
        Callable<String>  c = new Callable<String>() {

            public String call() throws Exception {
                String result = null;
                try {
                    engine.eval(script,sc);         
                } catch (ScriptException e) {
                    result = e.getMessage();
                } 
                return result;
            }
        };

        single.submit(c);
        single.shutdown();
        System.out.println("Java: ...submitted.");
    }   
}

javax.script.SimpleScriptContext java code examples, ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager. Rhino and concurrent access to javax.script.ScriptEngine. Chikei. Apparently, this user prefers to keep an air of mystery about them. 16. answers. 8. 3 Rhino and concurrent access to javax.script.ScriptEngine Mar 1 '12.

Multithreaded Javascript using Nashorn, ScriptEngine supporting the Rhino Javascript engine. It's worth noting that we can access anything from Java in Javascript including third is a bit of cool hackery * going on in this script using Java threads and concurrency. ScriptEngineFactory is used to describe and instantiate ScriptEngines. Each class implementing ScriptEngine has a corresponding factory that exposes metadata describing the engine class. The ScriptEngineManager uses the service provider mechanism described in the Jar File Specification to obtain instances of all ScriptEngineFactories available

Obtain a Script Engine, List of script engine factories by invoking its public This key's value describes the behavior of the script engine with respect to the concurrent execution of scripts. Mozilla Rhino Parameter javax.script.engine_version = 1.6 release 2 Parameter After obtaining a ScriptEngine instance, you can access its  Listing 1. Nashorn running along with Jython on the JVM. In the example in Listing 1, along with the ScriptEngine code, Nashorn is directly using the Java 8 Stream interface and expression closure supplied to forEach—if the function has a simple return type, the braces and return keyword may be omitted.

Nashorn: JavaScript made great in Java 8, Martin Heller takes the new Rhino's two command-line script runners for a test drive. All Core Java · Agile Development · Careers · Concurrency · Java · Java Platform JavaScript engines in Web browsers have access to the HTML that ScriptEngineManager to load the Nashorn script engine by name. ScriptEngineFactory is used to describe and instantiate ScriptEngines. Each class implementing ScriptEngine has a corresponding factory that exposes metadata describing the engine class. The ScriptEngineManager uses the service provider mechanism described in the Jar File Specification to obtain instances of all ScriptEngineFactories available in the current ClassLoader.

Comments
  • Are you binding the same objects?
  • If I was binding the same object for multiple executions, then obviously it'd be visible to all of them. But no, I'm not doing that.
  • What exactly then are you trying to figure out? If you are using different bindings with different bound objects what else in terms of state are you concerned with?
  • @wort - I was thinking more of state created or modified by the script, not passed into the script from Java. If I declare a global variable in the script, what is the scope of that variable? What happens if I modify some build-in global variable? Are these things specified by the javax.script API, or is it up to the engine implementers?
  • Is this what you are looking for?
  • What you are saying is not true at all. if you create separate script context for the same script engine instance and you specify the appropiate scope (ENGINE_SCOPE) you will not have this issue.