AbstractScriptModuleHandler: Recompile scripts on dependency change (#4922)

* AbstractScriptModuleHandler: Recompile scripts on dependency change

When a script's dependency changes, it should recompile the compiled script similarly to resetting the engine for uncompiled scripts.
Fixing this behaviour fixes an issue where compiled scripts stopped working after a dependency changed.

This also simplifies the code a bit.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
This commit is contained in:
Florian Hotze
2025-08-15 00:25:42 +02:00
committed by GitHub
parent 13ffa1c6ea
commit 2689f5f2dc
4 changed files with 36 additions and 13 deletions
@@ -17,6 +17,8 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.Action;
@@ -100,6 +102,11 @@ public class ScriptModuleHandlerFactory extends BaseModuleHandlerFactory impleme
if (handler != null) {
logger.debug("Resetting script engine for script {}", engineIdentifier);
handler.resetScriptEngine();
try {
handler.compile();
} catch (ScriptException e) {
logger.error("Failed to recompile script for rule {}: {}", handler.getRuleUID(), e.getMessage());
}
}
}
}
@@ -93,15 +93,10 @@ public abstract class AbstractScriptModuleHandler<T extends Module> extends Base
* {@link Compilable}.
*/
protected void compileScript() throws ScriptException {
if (compiledScript.isPresent()) {
return;
}
if (!scriptEngineManager.isSupported(this.type)) {
logger.debug(
"ScriptEngine for language '{}' could not be found, skipping compilation of script for identifier: {}",
type, engineIdentifier);
if (compiledScript.isPresent() || script.isEmpty()) {
return;
}
Optional<ScriptEngine> engine = getScriptEngine();
if (engine.isPresent()) {
ScriptEngine scriptEngine = engine.get();
@@ -119,11 +114,20 @@ public abstract class AbstractScriptModuleHandler<T extends Module> extends Base
/**
* Reset the script engine to force a script reload
*
*/
public synchronized void resetScriptEngine() {
scriptEngineManager.removeEngine(engineIdentifier);
scriptEngine = Optional.empty();
compiledScript = Optional.empty();
}
/**
* Gets the unique identifier of the rule this module handler is used for.
*
* @return the UID of the rule
*/
public String getRuleUID() {
return ruleUID;
}
/**
@@ -135,10 +139,20 @@ public abstract class AbstractScriptModuleHandler<T extends Module> extends Base
return engineIdentifier;
}
/**
* Get the script engine instance used by this module handler.
*
* @return the script engine instance if available, otherwise Optional.empty()
*/
protected Optional<ScriptEngine> getScriptEngine() {
return scriptEngine.isPresent() ? scriptEngine : createScriptEngine();
}
/**
* Creates a new script engine for the type defined in the module configuration.
*
* @return the script engine if available, otherwise Optional.empty()
*/
private Optional<ScriptEngine> createScriptEngine() {
ScriptEngineContainer container = scriptEngineManager.createScriptEngine(type, engineIdentifier);
@@ -203,13 +217,15 @@ public abstract class AbstractScriptModuleHandler<T extends Module> extends Base
}
/**
* Evaluates the passed script with the ScriptEngine.
* Evaluates the script with the given script engine.
*
* @param engine the script engine that is used
* @param script the script to evaluate
* @return the value returned from the execution of the script
*/
protected @Nullable Object eval(ScriptEngine engine, String script) {
protected @Nullable Object eval(ScriptEngine engine) {
if (script.isEmpty()) {
return null;
}
try {
if (compiledScript.isPresent()) {
logger.debug("Executing pre-compiled script of rule with UID '{}'", ruleUID);
@@ -91,7 +91,7 @@ public class ScriptActionHandler extends AbstractScriptModuleHandler<Action> imp
}
try {
setExecutionContext(scriptEngine, context);
Object result = eval(scriptEngine, script);
Object result = eval(scriptEngine);
resultMap.put("result", result);
resetExecutionContext(scriptEngine, context);
} finally { // Make sure that Lock is unlocked regardless of an exception being thrown or not to avoid
@@ -75,7 +75,7 @@ public class ScriptConditionHandler extends AbstractScriptModuleHandler<Conditio
}
try {
setExecutionContext(scriptEngine, context);
Object returnVal = eval(scriptEngine, script);
Object returnVal = eval(scriptEngine);
if (returnVal instanceof Boolean boolean1) {
result = boolean1;
} else {