When writing type methods of life cycle hooks in JavaScript it is sometimes convenient to call Java code. Perhaps you have existing code or a 3rd party library that is only available in Java or perhaps you want to start a long running background process on another thread. In such cases it is possible to add a jar file to Cordra and call code in that jar file from JavaScript.
Any jar files you wish to call should be placed into a directory called lib
in your Cordra
data directory. If the lib
directory doesn’t exist, create the lib
directory in the
Cordra data directory.
Consider the following simple Java class:
package net.example;
public class Point {
public double x;
public double y;
public double z;
public Point(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public String toString() {
return "(" + x + ", " + y + ", " + z + ")";
}
}
To create an instance of a Point and call its toString method from a type method in JavaScript you would do the following:
exports.staticMethods = {};
exports.staticMethods.exampleStaticMethod = exampleStaticMethod;
function exampleStaticMethod(context) {
const Point = Java.type("net.example.Point");
const point = new Point(1.3, 3.44, 2.58);
const result = {
point: point.toString()
};
return result;
}
More details on how to interact with Java objects from Cordra JavaScript can be found here https://www.graalvm.org/reference-manual/js/JavaInteroperability/ (for the newer GraalVM JavaScript engine) or here https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html (for the older Nashorn engine).
If you find yourself with a need to execute a long running Java background process you may want
to listen for a shutdown event when Cordra shuts down to cleanly terminate your code. Support
for this is provided by the class CordraHooksSupport
. The below show example code that uses
CordraHooksSupport
to listen to the Cordra shutdown event. It also uses CordraHooksSupport
to get an instance of a CordraClient that talks directly to the local Cordra instance.
package net.example.background;
import net.cnri.cordra.CordraHooksSupport;
import net.cnri.cordra.CordraHooksSupportProvider;
import net.cnri.cordra.api.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class BackgroundTasks {
private static BackgroundTasks instance = null;
private final ExecutorService exec;
private final CordraClient cordra;
private static volatile boolean shutdown = false;
private BackgroundTasks() {
exec = Executors.newSingleThreadExecutor();
CordraHooksSupport hooks = CordraHooksSupportProvider.get();
cordra = hooks.getCordraClient();
hooks.addShutdownHook(this::shutdown);
}
public synchronized static BackgroundTasks instance() {
if (instance == null) {
instance = new BackgroundTasks();
}
return instance;
}
public synchronized void shutdown() {
if (shutdown) {
return;
}
shutdown = true;
exec.shutdown();
try {
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
}
}
public synchronized void doBackgroundTask(String objectId) throws CordraException {
exec.submit(() -> {
try {
CordraObject obj = cordra.get(objectId);
//...
//Do some long running task with obj
//...
} catch (Exception e) {
}
});
}
}
And then from a JavaScript instance method you could start the above Java background process:
exports.methods = {};
exports.methods.exampleInstanceMethod = exampleInstanceMethod;
function exampleInstanceMethod(obj, context) {
const BackgroundTasks = Java.type("net.example.background.BackgroundTasks").;
const backgroundTasks = BackgroundTasks.instance();
backgroundTasks.doBackgroundTask(obj.id);
return "background task started started";
}