[jsscripting] Fix illegal multi-threaded access when using JSRule (#21093)

When using JSRule, the created SimpleRule is wrapper by ThreadsafeSimpleRuleDelegate to add the necessary synchronization.
If lock acquisition failed within the configured time or was interrupted,
it added the rule UID to the message, calling `delegate#getUID`,
which is overwritten from JS and thereby causes a GraalJS context access.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
This commit is contained in:
Florian Hotze
2026-07-02 20:35:25 +02:00
committed by GitHub
parent 68c1de8b06
commit 880b574376
@@ -43,6 +43,11 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
private final Lock lock;
private final long lockAcquisitionTimeoutMS;
private final SimpleRule delegate;
/**
* Get and store the UID upon wrapping as {@link SimpleRule#getUID()} is overwritten in openhab-js,
* which means calling it is a GraalJS context access and needs to be synchronized.
*/
private final String uid;
/**
* Constructor requires a lock object and delegate to forward invocations to.
@@ -55,6 +60,7 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
this.lock = lock;
this.lockAcquisitionTimeoutMS = lockAcquisitionTimeoutMS;
this.delegate = delegate;
this.uid = delegate.getUID();
}
@Override
@@ -66,7 +72,7 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting to acquire the lock for action '" + module.getId()
+ "' of rule '" + delegate.getUID() + '\'', e);
+ "' of rule '" + uid + '\'', e);
}
if (locked) {
try {
@@ -76,15 +82,14 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
lock.unlock();
}
} else {
throw new RuntimeException(
"Failed to acquire the lock for action '" + module.getId() + "' of rule '" + delegate.getUID()
+ "' within " + TimeUnit.MILLISECONDS.toSeconds(lockAcquisitionTimeoutMS) + " seconds.");
throw new RuntimeException("Failed to acquire the lock for action '" + module.getId() + "' of rule '" + uid
+ "' within " + TimeUnit.MILLISECONDS.toSeconds(lockAcquisitionTimeoutMS) + " seconds.");
}
}
@Override
public String getUID() {
return delegate.getUID();
return uid;
}
@Override