[jsscripting] Reimplement timer polyfills to conform standard JS (#13623)

* [jsscripting] Reimplement timers to conform standard JS
* [jsscripting] Name scheduled jobs by loggerName + id
* [jsscripting] Update timer identifiers
* [jsscripting] Update identifiers for scheduled jobs
* [jsscripting] Synchronize method that is called when the script is reloaded
* [jsscripting] Cancel all scheduled jobs when the engine is closed
* [jsscripting] Ensure that a timerId is never reused by a subsequent call & Use long primitive type instead of Integer
* [jsscripting] Use an abstraction class to inject features into the JS runtime
* [jsscripting] Make ThreadsafeTimers threadsafe for concurrent access to the class itself
* [jsscripting] Move the locking for `invokeFunction` to `OpenhabGraalJSScriptEngine`

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
This commit is contained in:
Florian Hotze
2022-11-05 15:26:46 +01:00
committed by GitHub
parent bbc744e3ff
commit 51d3fc211a
4 changed files with 254 additions and 136 deletions

View File

@@ -1,3 +1,4 @@
// ThreadsafeTimers is injected into the JS runtime
(function (global) {
'use strict';
@@ -5,8 +6,9 @@
// Append the script file name OR rule UID depending on which is available
const defaultIdentifier = "org.openhab.automation.script" + (globalThis["javax.script.filename"] ? ".file." + globalThis["javax.script.filename"].replace(/^.*[\\\/]/, '') : globalThis["ruleUID"] ? ".ui." + globalThis["ruleUID"] : "");
const System = Java.type('java.lang.System');
const ZonedDateTime = Java.type('java.time.ZonedDateTime');
const formatRegExp = /%[sdj%]/g;
// Pass the defaultIdentifier to ThreadsafeTimers to enable naming of scheduled jobs
ThreadsafeTimers.setIdentifier(defaultIdentifier);
function createLogger(name = defaultIdentifier) {
return Java.type("org.slf4j.LoggerFactory").getLogger(name);
@@ -162,61 +164,24 @@
},
// Allow user customizable logging names
// Be aware that a log4j2 required a logger defined for the logger name, otherwise messages won't be logged!
set loggerName(name) {
log = createLogger(name);
this._loggerName = name;
ThreadsafeTimers.setIdentifier(name);
},
get loggerName() {
return this._loggerName || defaultLoggerName;
return this._loggerName || defaultIdentifier;
}
};
function setTimeout(cb, delay) {
const args = Array.prototype.slice.call(arguments, 2);
return ThreadsafeTimers.createTimerWithArgument(
defaultIdentifier + '.setTimeout',
ZonedDateTime.now().plusNanos(delay * 1000000),
args,
function (args) {
cb.apply(global, args);
}
);
}
function clearTimeout(timer) {
if (timer !== undefined && timer.isActive()) {
timer.cancel();
}
}
function setInterval(cb, delay) {
const args = Array.prototype.slice.call(arguments, 2);
const delayNanos = delay * 1000000
let timer = ThreadsafeTimers.createTimerWithArgument(
defaultIdentifier + '.setInterval',
ZonedDateTime.now().plusNanos(delayNanos),
args,
function (args) {
cb.apply(global, args);
if (!timer.isCancelled()) {
timer.reschedule(ZonedDateTime.now().plusNanos(delayNanos));
}
}
);
return timer;
}
function clearInterval(timer) {
clearTimeout(timer);
}
// Polyfill common NodeJS functions onto the global object
globalThis.console = console;
globalThis.setTimeout = setTimeout;
globalThis.clearTimeout = clearTimeout;
globalThis.setInterval = setInterval;
globalThis.clearInterval = clearInterval;
globalThis.setTimeout = ThreadsafeTimers.setTimeout;
globalThis.clearTimeout = ThreadsafeTimers.clearTimeout;
globalThis.setInterval = ThreadsafeTimers.setInterval;
globalThis.clearInterval = ThreadsafeTimers.clearInterval;
// Support legacy NodeJS libraries
globalThis.global = globalThis;