I am looking for a reliable technique to manipulate javascript dates in scripting. I need to convert the current time to a specific timezone for a legacy system and I usually rely on the moment.js library for this. In this scenario is there a way to leverage native java date-handling techniques, or abesent that, a way to add additional javascript libraries to the server.
Bill,
Assuming youāre using Java 8 or later, the JavaScript engine that is embedded within the JVM and thus used by Camunda BPM is Nashorn. In Nashorn, you can load external JavaScript files using the load
statement, and then you can use those JavaScript files as desired.
I copied āmoment.jsā into my Spring Boot project directory under āsrc/main/resources/static/jsā, which allows me to load that āmoment.jsā file using either of the below statements:
load('http://localhost:8080/js/moment.js');
load('./project-name/src/main/resources/static/js/moment.js');
If you arenāt using Spring Boot, youāll either need to load moment.js from a web server a la the first bullet above or find your user directory and then construct a path that is relative to that. This can be done in Nashorn JavaScript - e.g. within a Camunda BPM Script Task - as shown below:
var System = Java.type('java.lang.System');
var userDir = System.getProperty('user.dir');
print('userDir: ' + userDir);
Once you have that user directory, you can use the load
statement as illustrated in the second bullet point above using the relative path to your āmoment.jsā file.
By the way, here are the very simple āmoment.jsā statements that I executed in my process model to test this:
var formattedCurrentDate = moment().format("dddd, MMMM Do YYYY, h:mm:ss a");
print('Formatted current date: ' + formattedCurrentDate);
I hope this helps!
-Ryan
Thanks, Ryan, this works well. Do you know if there is some kind of library caching or is there delay introduced as additional libraries are loaded?
Iām not certain. However, I do know that Camunda doesnāt compile or cache ECMAScript/JavaScript, so I would guess that it likely loads those external libraries each time.
Since Camunda doesnāt compile or cache JavaScript, the execution of JavaScript within Camunda process instances is quite slow. Thus, it may make sense to avoid using JavaScript Script Tasks in Camunda process models if performance is a significant concern.
-Ryan
@Bill_Powell itās using the standard nashorn javascript engine. Can see the oracle docs on it.
@ryans58 why do you think that Javascript is not cached. By https://docs.camunda.org/manual/7.11/user-guide/process-engine/scripting/#script-compilation it should be cached, since afaik nashorn implements Compilable.
@Ragnar is this comment out of date or originally misquoted?
This post outlines caching issues / perf issues with using nashorn on large scale jvm set ups
Very interesting topic.
Look at lines 100-121 in org.camunda.bpm.engine.impl.scripting.SourceExecutableScript
(tag: ā7.11.0ā):
public CompiledScript compile(ScriptEngine scriptEngine, String language, String src) {
if(scriptEngine instanceof Compilable && !scriptEngine.getFactory().getLanguageName().equalsIgnoreCase("ecmascript")) {
Compilable compilingEngine = (Compilable) scriptEngine;
try {
CompiledScript compiledScript = compilingEngine.compile(src);
LOG.debugCompiledScriptUsing(language);
return compiledScript;
} catch (ScriptException e) {
throw new ScriptCompilationException("Unable to compile script: " + e.getMessage(), e);
}
} else {
// engine does not support compilation
return null;
}
}
This suggests an explicit call-out of Nashorn/ECMAScript (JavaScript) as being not compilable.
This answer in StackOverflow suggests that Camunda could be compiling JavaScript and reusing it, providing of course that it setup a new Bindings
instance for each execution of the script (which Iām sure is being done anyway). Iād have to work through the code fully to know for sure, but this suggests that there is an opportunity for improvement in how Camunda handles script execution that could lead to improved performance.
-Ryan
Indeed, I didnāt know what a can of worms this would be. Meanwhile, Iāve revised my scripts to Groovy ājust in caseā. I do miss the native json handling that comes with javascript, but otherwise it was an easy transition. Thanks for the fascinating insights into the inner workings of Camunda.
Quick follow-up for anyone viewing an archive of this threadā¦
I modified Camunda BPM 7.11.0, removing the explicit call-out of ECMAScript/JavaScript that prevents compilation (modifying line 101 from the example above). I then ensured that it was compiling and caching JavaScript without any ill effects, and my tests succeeded. After that, I decided to run two 100 process instance performance tests with asynchronous continuations (ensuring multi-threading) comparing performance with and without compilation, and here are the results:
- The average execution time for the sample Script Task using JavaScript without compilation was 129 ms.
- The average execution time for the sample Script Task using JavaScript with compilation was 9.2 ms.
It would appear that JavaScript should be compiled and cached in Camunda BPM and that doing so yields a significant performance improvement for JavaScript execution.
-Ryan
Can you submit it as a PR to the camunda github repo?
Add your results and such. Possible that when nashorn was first added there was no support for compilation. (or other bugs that were unknown?)
Great work ryan
Iāve added a pull request for this change: https://github.com/camunda/camunda-bpm-platform/pull/360.
@ryans58 great! Do you have a Sample repo that can be added into the PR as a link that can be run as a unit test to show the differences of execution time?
To get accurate test results, this performance test must be run within a full Camunda BPM environment with the Job Executor running. I ran the test using a standard Tomcat-based Camunda BPM 7.11.0 (Community Edition) distribution, a simple process definition with a JavaScript Script Task, Postman Runner and a SQL query to extract the information. Of course, a test could also be run using Spring Boot or even a Spring Boot test.
Iāve created a repo with the modified source for the SourceExecutableScript
class (with the only change being the removal of the explicit ECMAScript call-out preventing compilation), the process definition used for the test and a README.md file for your review and the review of anyone else reviewing this post; that can all be found here.
Of course, Iād be happy to answer any questions anyone might have.
-Ryan