Properly close script engines in script transformation (#2932)

Signed-off-by: Jan N. Klug <github@klug.nrw>
pull/2933/head
J-N-K 2022-04-24 19:40:43 +02:00 committed by GitHub
parent b331066ea9
commit 3135017895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 37 additions and 2 deletions

View File

@ -14,6 +14,8 @@ package org.openhab.core.automation.module.script;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -25,6 +27,7 @@ import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.ThreadPoolManager;
import org.openhab.core.common.registry.RegistryChangeListener;
import org.openhab.core.transform.TransformationConfiguration;
import org.openhab.core.transform.TransformationConfigurationRegistry;
@ -54,6 +57,9 @@ public class ScriptTransformationService
private final Logger logger = LoggerFactory.getLogger(ScriptTransformationService.class);
private final ScheduledExecutorService scheduler = ThreadPoolManager
.getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON);
private final Map<String, ScriptEngineContainer> scriptEngineContainers = new HashMap<>();
private final Map<String, CompiledScript> compiledScripts = new HashMap<>();
private final Map<String, String> scriptCache = new HashMap<>();
@ -73,6 +79,11 @@ public class ScriptTransformationService
@Deactivate
public void deactivate() {
transformationConfigurationRegistry.removeRegistryChangeListener(this);
// cleanup script engines
scriptEngineContainers.values().stream().map(ScriptEngineContainer::getScriptEngine)
.forEach(this::disposeScriptEngine);
compiledScripts.values().stream().map(CompiledScript::getEngine).forEach(this::disposeScriptEngine);
}
@Override
@ -165,8 +176,32 @@ public class ScriptTransformationService
}
private void clearCache(String uid) {
compiledScripts.remove(uid);
scriptEngineContainers.remove(uid);
CompiledScript compiledScript = compiledScripts.remove(uid);
if (compiledScript != null) {
disposeScriptEngine(compiledScript.getEngine());
}
ScriptEngineContainer container = scriptEngineContainers.remove(uid);
if (container != null) {
disposeScriptEngine(container.getScriptEngine());
}
scriptCache.remove(uid);
}
private void disposeScriptEngine(ScriptEngine scriptEngine) {
if (scriptEngine instanceof AutoCloseable) {
// we cannot not use ScheduledExecutorService.execute here as it might execute the task in the calling
// thread (calling ScriptEngine.close in the same thread may result in a deadlock if the ScriptEngine
// tries to Thread.join)
scheduler.schedule(() -> {
AutoCloseable closeable = (AutoCloseable) scriptEngine;
try {
closeable.close();
} catch (Exception e) {
logger.error("Error while closing script engine", e);
}
}, 0, TimeUnit.SECONDS);
} else {
logger.trace("ScriptEngine does not support AutoCloseable interface");
}
}
}