Skip to content

Commit 2996267

Browse files
committed
Fix rules stay uninitialized when using Java 17
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>
1 parent 0e0adcb commit 2996267

File tree

1 file changed

+63
-28
lines changed
  • bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider

1 file changed

+63
-28
lines changed

bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider/ScriptModuleTypeProvider.java

+63-28
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414

1515
import java.util.ArrayList;
1616
import java.util.Collection;
17+
import java.util.LinkedList;
1718
import java.util.List;
1819
import java.util.Locale;
1920
import java.util.Map;
20-
import java.util.Optional;
2121
import java.util.TreeMap;
22-
import java.util.stream.Collectors;
23-
import java.util.stream.Stream;
2422

2523
import javax.script.ScriptEngine;
2624

@@ -42,6 +40,7 @@
4240
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
4341
import org.openhab.core.config.core.ParameterOption;
4442
import org.osgi.service.component.annotations.Component;
43+
import org.osgi.service.component.annotations.Deactivate;
4544
import org.osgi.service.component.annotations.Reference;
4645
import org.osgi.service.component.annotations.ReferenceCardinality;
4746
import org.osgi.service.component.annotations.ReferencePolicy;
@@ -59,12 +58,20 @@
5958
public class ScriptModuleTypeProvider implements ModuleTypeProvider {
6059

6160
private final Logger logger = LoggerFactory.getLogger(ScriptModuleTypeProvider.class);
61+
private final List<ProviderChangeListener<ModuleType>> listeners = new LinkedList<>();
6262
private final Map<String, String> parameterOptions = new TreeMap<>();
6363

64+
@Deactivate
65+
public void deactivate() {
66+
listeners.clear();
67+
}
68+
6469
@SuppressWarnings("unchecked")
6570
@Override
6671
public @Nullable ModuleType getModuleType(String UID, @Nullable Locale locale) {
67-
if (ScriptActionHandler.TYPE_ID.equals(UID)) {
72+
if (parameterOptions.isEmpty()) {
73+
return null;
74+
} else if (ScriptActionHandler.TYPE_ID.equals(UID)) {
6875
return getScriptActionType(locale);
6976
} else if (ScriptConditionHandler.TYPE_ID.equals(UID)) {
7077
return getScriptConditionType(locale);
@@ -73,26 +80,22 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider {
7380
}
7481
}
7582

76-
private @Nullable ModuleType getScriptActionType(@Nullable Locale locale) {
77-
if (parameterOptions.isEmpty()) {
78-
return null;
79-
} else {
80-
List<Output> outputs = new ArrayList<>();
81-
Output result = new Output("result", "java.lang.Object", "result", "the script result", null, null, null);
82-
outputs.add(result);
83-
return new ActionType(ScriptActionHandler.TYPE_ID, getConfigDescriptions(locale), "execute a given script",
84-
"Allows the execution of a user-defined script.", null, Visibility.VISIBLE, null, outputs);
85-
}
83+
private ModuleType getScriptActionType(@Nullable Locale locale) {
84+
List<Output> outputs = new ArrayList<>();
85+
Output result = new Output("result", "java.lang.Object", "result", "the script result", null, null, null);
86+
outputs.add(result);
87+
return new ActionType(ScriptActionHandler.TYPE_ID, getConfigDescriptions(locale), "execute a given script",
88+
"Allows the execution of a user-defined script.", null, Visibility.VISIBLE, null, outputs);
8689
}
8790

88-
private @Nullable ModuleType getScriptConditionType(@Nullable Locale locale) {
89-
if (parameterOptions.isEmpty()) {
90-
return null;
91-
} else {
92-
return new ConditionType(ScriptConditionHandler.TYPE_ID, getConfigDescriptions(locale),
93-
"a given script evaluates to true", "Allows the definition of a condition through a script.", null,
94-
Visibility.VISIBLE, null);
95-
}
91+
private ModuleType getScriptConditionType(@Nullable Locale locale) {
92+
return new ConditionType(ScriptConditionHandler.TYPE_ID, getConfigDescriptions(locale),
93+
"a given script evaluates to true", "Allows the definition of a condition through a script.", null,
94+
Visibility.VISIBLE, null);
95+
}
96+
97+
private List<ModuleType> getModuleTypesUnconditionally(@Nullable Locale locale) {
98+
return List.of(getScriptActionType(locale), getScriptConditionType(locale));
9699
}
97100

98101
/**
@@ -118,10 +121,7 @@ private List<ConfigDescriptionParameter> getConfigDescriptions(@Nullable Locale
118121

119122
@Override
120123
public Collection<ModuleType> getModuleTypes(@Nullable Locale locale) {
121-
return Stream
122-
.of(Optional.ofNullable(getScriptActionType(locale)),
123-
Optional.ofNullable(getScriptConditionType(locale)))
124-
.filter(Optional::isPresent).map(Optional::get).collect(Collectors.toUnmodifiableList());
124+
return parameterOptions.isEmpty() ? List.of() : getModuleTypesUnconditionally(locale);
125125
}
126126

127127
@Override
@@ -131,12 +131,40 @@ public Collection<ModuleType> getAll() {
131131

132132
@Override
133133
public void addProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
134-
// does nothing because this provider does not change
134+
synchronized (listeners) {
135+
listeners.add(listener);
136+
}
135137
}
136138

137139
@Override
138140
public void removeProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
139-
// does nothing because this provider does not change
141+
synchronized (listeners) {
142+
listeners.remove(listener);
143+
}
144+
}
145+
146+
private void notifyModuleTypesAdded() {
147+
List<ProviderChangeListener<ModuleType>> snapshot = null;
148+
synchronized (listeners) {
149+
snapshot = new LinkedList<>(listeners);
150+
}
151+
for (ModuleType moduleType : getModuleTypesUnconditionally(null)) {
152+
for (ProviderChangeListener<ModuleType> listener : snapshot) {
153+
listener.added(this, moduleType);
154+
}
155+
}
156+
}
157+
158+
private void notifyModuleTypesRemoved() {
159+
List<ProviderChangeListener<ModuleType>> snapshot = null;
160+
synchronized (listeners) {
161+
snapshot = new LinkedList<>(listeners);
162+
}
163+
for (ModuleType moduleType : getModuleTypesUnconditionally(null)) {
164+
for (ProviderChangeListener<ModuleType> listener : snapshot) {
165+
listener.removed(this, moduleType);
166+
}
167+
}
140168
}
141169

142170
/**
@@ -149,8 +177,12 @@ public void setScriptEngineFactory(ScriptEngineFactory engineFactory) {
149177
if (!scriptTypes.isEmpty()) {
150178
ScriptEngine scriptEngine = engineFactory.createScriptEngine(scriptTypes.get(0));
151179
if (scriptEngine != null) {
180+
boolean notifyListeners = parameterOptions.isEmpty();
152181
parameterOptions.put(getPreferredMimeType(engineFactory), getLanguageName(scriptEngine.getFactory()));
153182
logger.trace("ParameterOptions: {}", parameterOptions);
183+
if (notifyListeners) {
184+
notifyModuleTypesAdded();
185+
}
154186
} else {
155187
logger.trace("setScriptEngineFactory: engine was null");
156188
}
@@ -166,6 +198,9 @@ public void unsetScriptEngineFactory(ScriptEngineFactory engineFactory) {
166198
if (scriptEngine != null) {
167199
parameterOptions.remove(getPreferredMimeType(engineFactory));
168200
logger.trace("ParameterOptions: {}", parameterOptions);
201+
if (parameterOptions.isEmpty()) {
202+
notifyModuleTypesRemoved();
203+
}
169204
} else {
170205
logger.trace("unsetScriptEngineFactory: engine was null");
171206
}

0 commit comments

Comments
 (0)