From e77b1b0de0284ed3ebab43b13e0e154971cfce19 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Fri, 26 Jun 2026 10:17:58 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20"[jsscripting]=20Don't=20close=20the?= =?UTF-8?q?=20shared=20GraalJS=20engine=20on=20factory=20deacti=E2=80=A6"?= =?UTF-8?q?=20(#21054)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a4bef92da40f43c453ae738e4be1b741aaff824f. Signed-off-by: Florian Hotze --- .../internal/GraalJSScriptEngineFactory.java | 112 +++++------------- 1 file changed, 32 insertions(+), 80 deletions(-) diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java index 02af89ec2d..53287a5339 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java @@ -30,7 +30,6 @@ import org.openhab.core.automation.module.script.ScriptDependencyTracker; import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.config.core.ConfigurableService; import org.osgi.framework.Constants; -import org.osgi.service.component.ComponentConstants; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -44,7 +43,7 @@ import org.slf4j.event.Level; * An implementation of {@link ScriptEngineFactory} with customizations for GraalJS ScriptEngines. * * @author Jonathan Gilbert - Initial contribution - * @author Dan Cunningham - Script injections; Scope the shared Engine to the bundle lifetime + * @author Dan Cunningham - Script injections * @author Florian Hotze - Debugger support */ @Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.jsscripting", property = Constants.SERVICE_PID @@ -67,16 +66,10 @@ public class GraalJSScriptEngineFactory implements ScriptEngineFactory { private final Logger logger = LoggerFactory.getLogger(GraalJSScriptEngineFactory.class); private final GraalJSScriptEngineConfiguration configuration; - /** - * Shared Polyglot {@link Engine} used by all {@link OpenhabGraalJSScriptEngine} instances. - *

- * Kept static and never closed on deactivation. Script contexts created from it can outlive a component restart - * (e.g. when an OSGi reference is rebound), and closing the engine would break every loaded rule with - * "The Context is already closed". So we create it once and reuse it for the life of the bundle. + * Shared Polyglot {@link Engine} instance to be used by all instances of {@link OpenhabGraalJSScriptEngine}. */ - private static volatile @Nullable Engine engine; - private static final Object ENGINE_LOCK = new Object(); + private final Engine engine; private final JSScriptServiceUtil jsScriptServiceUtil; private final JSDependencyTracker jsDependencyTracker; @@ -90,53 +83,35 @@ public class GraalJSScriptEngineFactory implements ScriptEngineFactory { this.jsScriptServiceUtil = jsScriptServiceUtil; this.configuration = new GraalJSScriptEngineConfiguration(config); - if (getEngine(configuration, logger) == null || getLanguage() == null) { + if (configuration.isDebuggerEnabled()) { + Engine.Builder engineBuilder = createEngineBuilder(); + engineBuilder // + .option("inspect", "0.0.0.0:" + configuration.getDebuggerPort()) // + .option("inspect.Suspend", "false") // Don't pause at startup waiting for debugger to attach + .option("inspect.WaitAttached", "false") // Don't block code execution waiting for debugger to + // attach + .option("inspect.Secure", "false"); // Disable TLS + Engine engine; + try { + engine = engineBuilder.build(); + } catch (RuntimeException e) { + logger.error( + "Failed to initialize Graal JavaScript engine with debugger support. Continuing without debugger support.", + e); + engine = createEngineBuilder().build(); + } + logger.info("Debugger support is enabled for JavaScript Scripting."); + this.engine = engine; + } else { + this.engine = createEngineBuilder().build(); + } + + if (getLanguage() == null) { logger.error(LANG_NOT_INITIALIZED_MSG); } } - /** - * Creates the shared {@link Engine} on first use and reuses it afterwards. Debugger settings are read once here, - * so changing them only takes effect after an openHAB restart. - * - * @return the shared {@link Engine}, or {@code null} if it could not be created - */ - private static @Nullable Engine getEngine(GraalJSScriptEngineConfiguration configuration, Logger logger) { - synchronized (ENGINE_LOCK) { - Engine localEngine = engine; - if (localEngine != null) { - return localEngine; - } - try { - if (configuration.isDebuggerEnabled()) { - Engine.Builder engineBuilder = createEngineBuilder() // - .option("inspect", "0.0.0.0:" + configuration.getDebuggerPort()) // - .option("inspect.Suspend", "false") // Don't pause at startup waiting for debugger to attach - .option("inspect.WaitAttached", "false") // Don't block code execution waiting for debugger - // to attach - .option("inspect.Secure", "false"); // Disable TLS - try { - localEngine = engineBuilder.build(); - } catch (RuntimeException e) { - logger.error( - "Failed to initialize Graal JavaScript engine with debugger support. Continuing without debugger support.", - e); - localEngine = createEngineBuilder().build(); - } - logger.info("Debugger support is enabled for JavaScript Scripting."); - } else { - localEngine = createEngineBuilder().build(); - } - } catch (RuntimeException e) { - logger.error("Failed to initialize Graal JavaScript engine.", e); - return null; - } - engine = localEngine; - return localEngine; - } - } - - private static Engine.Builder createEngineBuilder() { + private Engine.Builder createEngineBuilder() { Logger engineLogger = LoggerFactory .getLogger(GraalJSScriptEngineFactory.class.getPackageName() + ".org.graalvm.polyglot.Engine"); return Engine.newBuilder().allowExperimentalOptions(true) // @@ -148,30 +123,9 @@ public class GraalJSScriptEngineFactory implements ScriptEngineFactory { .err(new ThreadLocalSlf4jOutputStream(engineLogger, Level.DEBUG)); } - /** - * Releases the shared {@link Engine}, but only when the bundle is actually stopping. The reason is checked so that - * ordinary component restarts (e.g. an OSGi reference rebind) do not close the engine and break running scripts - - * that is exactly what this fix prevents. Only a real bundle stop releases the engine. - */ @Deactivate - protected void deactivate(int reason) { - if (reason == ComponentConstants.DEACTIVATION_REASON_BUNDLE_STOPPED) { - dispose(); - } - } - - /** - * Closes the shared {@link Engine} and clears it so it is re-created on next use. Called on bundle stop and from - * tests. - */ public void dispose() { - synchronized (ENGINE_LOCK) { - Engine localEngine = engine; - if (localEngine != null) { - localEngine.close(); - engine = null; - } - } + this.engine.close(); } @Modified @@ -194,13 +148,12 @@ public class GraalJSScriptEngineFactory implements ScriptEngineFactory { if (!SCRIPT_TYPES.contains(scriptType)) { return null; } - Engine localEngine = getEngine(configuration, logger); - if (localEngine == null || getLanguage() == null) { + if (getLanguage() == null) { logger.error(LANG_NOT_INITIALIZED_MSG); return null; } return new DebuggingGraalScriptEngine<>( - new OpenhabGraalJSScriptEngine(configuration, localEngine, jsScriptServiceUtil, jsDependencyTracker)); + new OpenhabGraalJSScriptEngine(configuration, engine, jsScriptServiceUtil, jsDependencyTracker)); } @Override @@ -214,7 +167,6 @@ public class GraalJSScriptEngineFactory implements ScriptEngineFactory { * @return the Graal language of {@link OpenhabGraalJSScriptEngine} or {@code null} if not available */ private @Nullable Language getLanguage() { - Engine localEngine = engine; - return localEngine == null ? null : localEngine.getLanguages().get(OpenhabGraalJSScriptEngine.LANGUAGE_ID); + return engine.getLanguages().get(OpenhabGraalJSScriptEngine.LANGUAGE_ID); } }