How to improve JDK 8 nashorn engine performance? - java

We have a requirement where we need to execute several short JavaScript snippets via Java. For that we are using the Nashorn engine that comes embedded along with Java. Java version is 1.8.0_191.
We initialize the script engine only once in the life cycle of the program. Then we pre-compile the snippets (as the snippets will repeat) in cache. We use pre-compiled scripts for eval. The bindings will be different each time and those are created before every execution.
Following is the snippet.
public class RuleExecutor {
private ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
private Map<String, CompiledScript> ruleCache = new HashMap<>();
..
....
public Object execute(Rule rule) {
Bindings bindings = engine.createBindings();
bindings.put(....);
compiled = ruleCache.get(rule.getTarget());
if (compiled == null) {
compiled = ((Compilable) engine).compile(rule.getExpr());
ruleCache.put(rule.getTarget(), compiled);
}
compiled.eval(bindings);
output = bindings.get(rule.getTarget());
return output;
}
The rule expressions can be arbitrary self contained scripts. However they repeat, hence the caching.
With the above, we are getting 120000 executions per minute.
How do we expedite the executions ?

Performance improved greatly on creating the Bindings only once during the program life cycle. After each execution, the bindings need to be cleared using bindings.clear()

Related

Is it possible to run playwright script (written in TS) through Java code? [duplicate]

I want to run JavaScript code at the server side. I want to manipulate result returned by JavaScript inside my Java code. How can it be done?
The start is clearly to look into rhino.
I think you will find this 3 links very useful
JavaScript EE, Part 1: Run JavaScript files on the server side
JavaScript EE, Part 2: Call remote JavaScript functions with Ajax
JavaScript EE, Part 3: Use Java scripting API with JSP
You can also have a look to helma
Helma is a server-side Javascript environment and web application framework for fast and efficient scripting and serving of your websites and Internet applications.
Helma is written in Java and employs Javascript for its server-side scripting environment ...
The other answers are correct that if you want to execute Javascript on the server side, you'd need to evaluate it in the context of a JS runtime.
However, I'm not convinced that this is exactly what you're asking. I think there may be a chance that you want to run some "typical" JS functionality that relates to how the page is displayed on the client's machine or interacted with on the client - and that will not be possible to run on the server side.
As a concrete examples:
If you want to run some kind of algorithm in JS without porting it to Java - say, you have some opaque Javascript code that generates a particular sequence given a seed - this would work fine if you run it on Rhino on the server.
If you want to invoke a Javascript function while creating the page, rather than while it's running - say, to get the user's colour depth/screen resolution and change how the page is generated - then this will not be possible from the server, as there is no client at this point to query.
Broadly speaking, any Javascript that involves document or navigator or even any elements of the page itself, is likely to fall into the latter category.
If you really need to get information about the client environment in order to control how the page is rendered, this must be extracted from the client on the previous page, and encoded into the request (as query parameters or form data). These parameters can then be read directly on the server and used to control the output.
Remember that when your code is running on the server side, you're creating a page (ultimately a bunch of HTML, CSS and JS) that will be sent to the client once it's done - at this point there is no client yet and so you can't interact with them.
Apologies if I've got the wrong end of the stick on this one, but this type of question is typically asked by people who haven't grasped the client/server separation.
You need a JS runtime inside of a Java runtime. One way to do this is Rhino
You execute the JavaScript with Rhino, a JavaScript library for Java.
You can use RHINO or NASHORN.
public class RhinoApp {
private String simpleAdd = "var z=9; z*=9";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
This example should clearly state how to load, evaluate and execute a Javascript function in Java:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
URI source_js = JavascriptExecutor.class.getResource("/file.js").toURI();
String source_text = Files.readAllLines(Paths.get(source_js)).stream().collect(Collectors.joining("\n"));
engine.eval(source_text);
Invocable inv = (Invocable) engine;
Object returnValue = inv.invokeFunction("functionJsName", "functionJsParameter");
System.out.println(returnValue.toString());

How to determine root cause of sluggish Nashorn performance or bottleneck with pre-compiled scripts

I'm having some sluggish performance in Nashorn that I can't really explain the cause of. I'll detail what my setup is and how I've tried to debug it.
Hardware:
Fairly decent server hardware ('13 era - 12 core Xeon, 2.1GHz). 64GB DDR3 RAM.
Software:
Oracle JDK8 (latest 64-bit) (40GB RAM pre-allocated to the JVM).
My implementation is:
Multiple Nashorn ScriptEngine instances, each with a pre-compiled "utility.js" that provides some helper functions that user-defined scripts can utilise.
I have a pool of ScriptEngine objects all ready to go with the utility.js already compiled against them and a thread allocator which will spin up threads up to a set limit. Each thread will grab a pre-allocated ScriptEngine and eval the user JS to it using a new context and execute it/store the result somewhere before returning the ScriptEngine to the pool. This is all working great and if my user script is fairly simple (single function) it's blindingly fast.
However, most user scripts are rather large and of the form:
function myFunc() {
myFunc1();
myFunc2();
... (you get the picture, they define and call a lot of functions!)
myFunc100();
}
function myFunc1() {
// do something simple here
}
When run in parallel, say with 25 threads at a time, and each with their own ScriptEngine (and all the pre-compiled things mentioned above) will take a very long time to execute whilst showing little CPU use (8-10% total) and no major blocking in jmc/jvisualvm. The threads will show that they've blocked a fair amount (count wise), but the slices are so minute I can never see them when clicking through the threads.
Most of the time when I click through the threads they're all showing that they're in MethodHandleNatives.setCallSiteTargetNormal.
I've tried a few things:
1. Single engine, different contexts. I could see blocking here between my threads even though it was all pre-compiled. The threads would wait (as they should) before calling individual snippets of bytecode from what I could tell. This isn't a viable solution.
Tried to inline a bunch of functions (most but not all) in the user scripts, this still didn't increase CPU usage and most threads were still in MethodHandleNatives.setCallSiteTargetNormal. Even inlined functions still seem to be heading to MethodHandleNatives.setCallSiteTargetNormal if I inspected the stack traces.
Here's how I create the ScriptEngines and pre-stuff them with the "utility.js" (the code where I stuff them into a pool has omitted to keep it short):
/**
* Creates a PreCompiledScriptEngine which will contain a ScriptEngine + Pre-compiled utility.js
*/
private PreCompiledScriptEngine createScriptEngine() {
String source = new Scanner(this.getClass().getClassLoader().getResourceAsStream(UTILITY_SCRIPT)).useDelimiter("\\Z").next();
try {
totalEngines.getAndAdd(1);
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine();
return new PreCompiledScriptEngine(engine, ((Compilable) engine).compile(source));
}
catch (ScriptException e) {
Logger.error(e);
}
return null;
}
/**
* Small helper class to group a ScriptEngine and a CompiledScript (of utility.js) together
*/
public class PreCompiledScriptEngine {
private ScriptEngine scriptEngine;
private CompiledScript compiledScript;
PreCompiledScriptEngine(ScriptEngine scriptEngine, CompiledScript compiledScript) {
this.scriptEngine = scriptEngine;
this.compiledScript = compiledScript;
}
public ScriptEngine getScriptEngine() {
return scriptEngine;
}
/**
* This method will return the utility.js compiled runtime against our engine.
*
* #return CompiledScript version of utility.js
*/
public CompiledScript getCompiledScript() {
return compiledScript;
}
}
And here's how I execute user specific JavaScript:
public Object executeUserScript(String script, String scriptFunction, Object[] parameters) {
try {
// Create a brand new context
PreCompiledScriptEngine preCompiledScriptEngine = obtainFromMyScriptEnginePool();
ScriptEngine engine = preCompiledScriptEngine.getScriptEngine();
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
// Evaluate the pre-compiled utility.js in our new context
preCompiledScriptEngine.getCompiledScript().eval(context);
// Evaluate the specific user script in this context too
engine.eval(script, context);
//get the JS function the user wants to call
JSObject jsObject = (JSObject) context.getAttribute(scriptFunction, ScriptContext.ENGINE_SCOPE);
// Call the JS function with the parameters
return jsObject.call(null, parameters);
}
catch (ScriptException e) {
Logger.error("generated", e);
throw new RuntimeException(e.getMessage());
}
}
What I'm expecting out of this is that CPU usage would be 100% if my thread pool was exhausting the resources available on the machine and showing low performance, but instead I'm seeing low CPU and low performance :( I can't quite pin down where I'm going wrong here or why it's so slow without any obvious drain on resources.
One thing I just noticed while going to grab a stack trace from JVisualVM is that all my threads appear to be exhibiting this scenario: I allow the user defined Java Script to call a utility.js function which is essentially "execute another script", the stack traces all appear to be from this nested call to another script. In my setup it'd be using the same thread, and same engine from the thread again with a new context. I'd think this would be the same as before though and not require further compiling?
Related articles I've already looked at:
What is the difference between anonymous and inline functions in JavaScript?
and
Nashorn inefficiency
Edit:
Looking deeper into this, it's mostly when eval() occurs from inside a compiled script, but not all the time, something about specific cases must be making it unable to just be reinvoked directly without calling setTarget() which ends up taking more time.
The interesting thing is that the threads don't show that they're blocking when they're making these calls down to native JVM methods so it's hard to see where the time is being spent in every tool I've looked at.

Running Talend Job from within Java application

I am developing a web app using Spring MVC. Simply put, a user uploads a file which can be of different types (.csv, .xls, .txt, .xml) and the application parses this file and extracts data for further processing. The problem is that I format of the file can change frequently. So there must be some way for quick and easy customization. Being a bit familiar with Talend, I decided to give it a shot and use it as ETL tool for my app. This short tutorial shows how to run Talend job from within Java app - http://www.talendforge.org/forum/viewtopic.php?id=2901
However, jobs created using Talend can read from/write to physical files, directories or databases. Is it possible to modify Talend job so that it can be given some Java object as a parameter and then return Java object just as usual Java methods?
For example something like:
String[] param = new String[]{"John Doe"};
String talendJobOutput = teaPot.myjob_0_1.myJob.main(param);
where teaPot.myjob_0_1.myJob is the talend job integrated into my app
I did something similar I guess. I created a mapping in tallend using tMap and exported this as talend job (java se programm). If you include the libraries of that job, you can run the talend job as described by others.
To pass arbitrary java objects you can use the following methods which are present in every talend job:
public Object getValueObject() {
return this.valueObject;
}
public void setValueObject(Object valueObject) {
this.valueObject = valueObject;
}
In your job you have to cast this object. e.g. you can put in a List of HashMaps and use Java reflection to populate rows. Use tJavaFlex or a custom component for that.
Using this method I can adjust the mapping of my data visually in Talend, but still use the generated code as library in my java application.
Now I better understand your willing, I think this is NOT possible because Talend's architecture is made like a standalone app, with a "main" entry point merely as does the Java main() method :
public String[][] runJob(String[] args) {
int exitCode = runJobInTOS(args);
String[][] bufferValue = new String[][] { { Integer.toString(exitCode) } };
return bufferValue;
}
That is to say : the Talend execution entry point only accepts a String array as input and doesn't returns anything as output (except as a system return code).
So, you won't be able link to Talend (generated) code as a library but as an isolated tool that you can only parameterize (using context vars, see my other response) before launching.
You can see that in Talend help center or forum the only integration described is as an "external" job execution ... :
Talend knowledge base "Calling a Talend Job from an external Java application" article
Talend Community Forum "Java Object to Talend" topic
May be you have to rethink the architecture of your application if you want to use Talend as the ETL tool for your purpose.
Now from Talend ETL point of view : if you want to parameter the execution environment of your Jobs (for exemple the physical directory of the uploaded files), you should use context variables that can be loaded at execution time from a configuration file as mentioned here :
https://help.talend.com/display/TalendOpenStudioforDataIntegrationUserGuide53EN/2.6.6+Context+settings

Use ScriptEngine to bind global javascript functions from java code [duplicate]

The JSR223 Bindings class allows you to expose arbitrary Java objects to scripting languages. But they have to be objects. I would like to define a function quit() that can be called from the scripting environment that turns into quitObject.run() in Java. But JSR223 doesn't define the concept of a function object. Is there a language-independent way to do the following in Javascript, namely to take a Runnable() and create a function in the scripting environment?
static private Object asFunction(ScriptEngine engine, Runnable r)
throws ScriptException
{
final Bindings bindings = engine.createBindings();
bindings.put("r", r);
return engine.eval(
"(function (r) { var f = function() { r.run(); }; return f;})(r)",
bindings);
}
Runnable quitObject = /* get/create a Runnable here */
Bindings bindings = engine.createBindings();
bindings.put("quit", asFunction(engine, quitObject));
With the builtin Javascript support for JSR223 this creates a sun.org.mozilla.javascript.internal.InterpretedFunction which does what I want. But it obviously won't work in Jython or whatever, and I'd like to make this language-independent.
I don't want my script users to have to type quitObject.run() as that's clumsy, and I don't want to parse script input to find quit() as it could be buried within other code.
If you look at javascript engine source code you'll find how oracle/sun implemented 2 functions (print, and println) which are magically (or not so magically) present when you fire up your engine.
Those function are 'scripted' , which is more or less what you did.
What I would do is : load and evaluate a bootstrap.[language_extension] before evaluating any other input in the new context.
You could easily create such scripts for each language you intend to support.

How to pass an object from Java to JavaScript using Rhino

I am working on a Java program where an object needs to have user-customization behavior for one function. I am implementing this using Mozilla Rhino, JavaScript and Java.
I cannot figure out how to take the already instantiated object and pass it to a pre-written script.
I have looked through many tutorials on Rhino, and none have given an example like this. Any advice or links would be greatly appreciated.
Thank you for your time.
This answer to another question passes an object, data, from Java to Rhino Javascript.
I have no idea whether or not it works (well I suppose it does). Here are the relevant parts:
public static class data {
Double value = 1.0d;
}
ScriptEngine engine = new ScriptEngineManager().getEngineByName ("rhino");
data data = new data();
Context.enter().getWrapFactory().setJavaPrimitiveWrap(false);
engine.eval("function test(data) { return data.get('value1') + 5;};");
System.out.println("Result:" + ((Invocable)engine).invokeFunction("test", data));
(I didn't know about that setJavaPrimitiveWrap(), here is some WrapFactory Javadoc.)

Categories

Resources