Javascript Date Manipulation

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! :slight_smile:

-Ryan

1 Like

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. :slight_smile:

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

1 Like

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

1 Like

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?

@StephenOTT,

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

2 Likes