Fix rules stay uninitialized when using Java 17 (#2787)

On Java 17 there is no Nashorn scripting engine so it takes a bit longer before ScriptEngines are available.
Rules would stay uninitialized forever because the ScriptModuleTypeProvider did not notify its listeners whenever script.ScriptAction, script.ScriptCondition became available.

Signed-off-by: Wouter Born <github@maindrain.net>
pull/2795/head
Wouter Born 2022-02-20 20:42:32 +01:00 committed by GitHub
parent 5e33cfc26a
commit 2e0b242099
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 42 additions and 34 deletions

View File

@ -17,10 +17,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
@ -36,12 +33,13 @@ import org.openhab.core.automation.type.ConditionType;
import org.openhab.core.automation.type.ModuleType; import org.openhab.core.automation.type.ModuleType;
import org.openhab.core.automation.type.ModuleTypeProvider; import org.openhab.core.automation.type.ModuleTypeProvider;
import org.openhab.core.automation.type.Output; import org.openhab.core.automation.type.Output;
import org.openhab.core.common.registry.ProviderChangeListener; import org.openhab.core.common.registry.AbstractProvider;
import org.openhab.core.config.core.ConfigDescriptionParameter; import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ConfigDescriptionParameter.Type; import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder; import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
import org.openhab.core.config.core.ParameterOption; import org.openhab.core.config.core.ParameterOption;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicy;
@ -56,15 +54,23 @@ import org.slf4j.LoggerFactory;
*/ */
@NonNullByDefault @NonNullByDefault
@Component @Component
public class ScriptModuleTypeProvider implements ModuleTypeProvider { public class ScriptModuleTypeProvider extends AbstractProvider<ModuleType> implements ModuleTypeProvider {
private final Logger logger = LoggerFactory.getLogger(ScriptModuleTypeProvider.class); private final Logger logger = LoggerFactory.getLogger(ScriptModuleTypeProvider.class);
private final Map<String, String> parameterOptions = new TreeMap<>(); private final Map<String, String> parameterOptions = new TreeMap<>();
@Deactivate
public void deactivate() {
listeners.clear();
parameterOptions.clear();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public @Nullable ModuleType getModuleType(String UID, @Nullable Locale locale) { public @Nullable ModuleType getModuleType(String UID, @Nullable Locale locale) {
if (ScriptActionHandler.TYPE_ID.equals(UID)) { if (parameterOptions.isEmpty()) {
return null;
} else if (ScriptActionHandler.TYPE_ID.equals(UID)) {
return getScriptActionType(locale); return getScriptActionType(locale);
} else if (ScriptConditionHandler.TYPE_ID.equals(UID)) { } else if (ScriptConditionHandler.TYPE_ID.equals(UID)) {
return getScriptConditionType(locale); return getScriptConditionType(locale);
@ -73,26 +79,22 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
} }
} }
private @Nullable ModuleType getScriptActionType(@Nullable Locale locale) { private ModuleType getScriptActionType(@Nullable Locale locale) {
if (parameterOptions.isEmpty()) { List<Output> outputs = new ArrayList<>();
return null; Output result = new Output("result", "java.lang.Object", "result", "the script result", null, null, null);
} else { outputs.add(result);
List<Output> outputs = new ArrayList<>(); return new ActionType(ScriptActionHandler.TYPE_ID, getConfigDescriptions(locale), "execute a given script",
Output result = new Output("result", "java.lang.Object", "result", "the script result", null, null, null); "Allows the execution of a user-defined script.", null, Visibility.VISIBLE, null, outputs);
outputs.add(result);
return new ActionType(ScriptActionHandler.TYPE_ID, getConfigDescriptions(locale), "execute a given script",
"Allows the execution of a user-defined script.", null, Visibility.VISIBLE, null, outputs);
}
} }
private @Nullable ModuleType getScriptConditionType(@Nullable Locale locale) { private ModuleType getScriptConditionType(@Nullable Locale locale) {
if (parameterOptions.isEmpty()) { return new ConditionType(ScriptConditionHandler.TYPE_ID, getConfigDescriptions(locale),
return null; "a given script evaluates to true", "Allows the definition of a condition through a script.", null,
} else { Visibility.VISIBLE, null);
return new ConditionType(ScriptConditionHandler.TYPE_ID, getConfigDescriptions(locale), }
"a given script evaluates to true", "Allows the definition of a condition through a script.", null,
Visibility.VISIBLE, null); private List<ModuleType> getModuleTypesUnconditionally(@Nullable Locale locale) {
} return List.of(getScriptActionType(locale), getScriptConditionType(locale));
} }
/** /**
@ -118,10 +120,7 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
@Override @Override
public Collection<ModuleType> getModuleTypes(@Nullable Locale locale) { public Collection<ModuleType> getModuleTypes(@Nullable Locale locale) {
return Stream return parameterOptions.isEmpty() ? List.of() : getModuleTypesUnconditionally(locale);
.of(Optional.ofNullable(getScriptActionType(locale)),
Optional.ofNullable(getScriptConditionType(locale)))
.filter(Optional::isPresent).map(Optional::get).collect(Collectors.toUnmodifiableList());
} }
@Override @Override
@ -129,14 +128,16 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
return getModuleTypes(null); return getModuleTypes(null);
} }
@Override private void notifyModuleTypesAdded() {
public void addProviderChangeListener(ProviderChangeListener<ModuleType> listener) { for (ModuleType moduleType : getModuleTypesUnconditionally(null)) {
// does nothing because this provider does not change notifyListenersAboutAddedElement(moduleType);
}
} }
@Override private void notifyModuleTypesRemoved() {
public void removeProviderChangeListener(ProviderChangeListener<ModuleType> listener) { for (ModuleType moduleType : getModuleTypesUnconditionally(null)) {
// does nothing because this provider does not change notifyListenersAboutRemovedElement(moduleType);
}
} }
/** /**
@ -149,8 +150,12 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
if (!scriptTypes.isEmpty()) { if (!scriptTypes.isEmpty()) {
ScriptEngine scriptEngine = engineFactory.createScriptEngine(scriptTypes.get(0)); ScriptEngine scriptEngine = engineFactory.createScriptEngine(scriptTypes.get(0));
if (scriptEngine != null) { if (scriptEngine != null) {
boolean notifyListeners = parameterOptions.isEmpty();
parameterOptions.put(getPreferredMimeType(engineFactory), getLanguageName(scriptEngine.getFactory())); parameterOptions.put(getPreferredMimeType(engineFactory), getLanguageName(scriptEngine.getFactory()));
logger.trace("ParameterOptions: {}", parameterOptions); logger.trace("ParameterOptions: {}", parameterOptions);
if (notifyListeners) {
notifyModuleTypesAdded();
}
} else { } else {
logger.trace("setScriptEngineFactory: engine was null"); logger.trace("setScriptEngineFactory: engine was null");
} }
@ -166,6 +171,9 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
if (scriptEngine != null) { if (scriptEngine != null) {
parameterOptions.remove(getPreferredMimeType(engineFactory)); parameterOptions.remove(getPreferredMimeType(engineFactory));
logger.trace("ParameterOptions: {}", parameterOptions); logger.trace("ParameterOptions: {}", parameterOptions);
if (parameterOptions.isEmpty()) {
notifyModuleTypesRemoved();
}
} else { } else {
logger.trace("unsetScriptEngineFactory: engine was null"); logger.trace("unsetScriptEngineFactory: engine was null");
} }