Playing around with the Nashorn JavaScript engine

As my day-job is typically writing (modern) enterprise Java applications I have an open ear for news in the Java world.
Luckily I can currently do all of my work with the most recent version of Java (which means currently Java 8). Apart from some nice features like lambdas, Java 8 also brought us the Nashorn (oracle post, openjdk post) JavaScript engine.

Although having heard a bit about it and read some short examples, I’ve never actually played with it until a few days ago.
Here’s what I tried.

Creating an instance of an engine is easy:

public TestScriptExecutor() {
ScriptEngineManager engineManager = new ScriptEngineManager();
engine = (NashornScriptEngine)
engineManager.getEngineByName("nashorn");
}

Next we need to load up some JavaScript and set up the context, meaning additional objects that should be in scope:

public void loadScript(String script) {
Bindings bindings =
engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("ui", this);
try {
engine.compile(script).eval(bindings);
} catch (ScriptException e) {
LOG.warn("scripterror: ", e);
}
}

That’s basically it, now we can already call any of the functions defined in the JavaScript code we are running:

public void invokeScriptFunction(String functionName) {
try {
LOG.info("trying to run: {}", functionName);
engine.invokeFunction(functionName);
} catch (ScriptException e) {
LOG.warn("script error!");
} catch (NoSuchMethodException e) {
LOG.info("method not defined");
}
}

Boom, that was easy :)

You can find the full runnable example script with the input-script here in this GitHub Gist.

Here is another snippet of how i tried to set up the context before, which didn’t work at all — haven’t dug into what exactly is wrong about this, so if someone knows, please let me know in the comments, thanks!

// setting up variables for JS did NOT work like this:
final SimpleScriptContext context = new SimpleScriptContext();
context.setAttribute("ui", this, ScriptContext.ENGINE_SCOPE);
engine.setContext(context);
engine.compile(script).eval(context);
// ... call a function, the ui variable was not bound inside the
// js code then

I have to say I was really positively surprised how easy it was in the end to integrate the code. I know the example is simple and minimal, however for such a technically complex thing as integrating two languages with vastly different paradigms, I had expected a lot more rough edges :)