mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Implemented start level service (#1914)
Fixes #1637 Fixes #1777 Fixes #1734 Fixes #1823 Fixes #1808 Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
+30
-9
@@ -39,11 +39,18 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.automation.module.script.ScriptEngineContainer;
|
||||
import org.openhab.core.automation.module.script.ScriptEngineManager;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.service.AbstractWatchService;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyMarkerFilter;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
@@ -57,40 +64,41 @@ import org.osgi.service.component.annotations.Reference;
|
||||
* @author Kai Kreuzer - improved logging and removed thread pool
|
||||
*/
|
||||
@Component(immediate = true)
|
||||
public class ScriptFileWatcher extends AbstractWatchService {
|
||||
public class ScriptFileWatcher extends AbstractWatchService implements ReadyTracker {
|
||||
|
||||
private static final Set<String> EXCLUDED_FILE_EXTENSIONS = new HashSet<>(
|
||||
Arrays.asList("txt", "old", "example", "backup", "md", "swp", "tmp", "bak"));
|
||||
private static final String FILE_DIRECTORY = "automation" + File.separator + "jsr223";
|
||||
private static final long INITIAL_DELAY = 25;
|
||||
private static final long RECHECK_INTERVAL = 20;
|
||||
|
||||
private final long earliestStart = System.currentTimeMillis() + INITIAL_DELAY * 1000;
|
||||
private boolean started = false;
|
||||
|
||||
private final ScriptEngineManager manager;
|
||||
private final ReadyService readyService;
|
||||
private @Nullable ScheduledExecutorService scheduler;
|
||||
|
||||
private final Map<String, Set<URL>> urlsByScriptExtension = new ConcurrentHashMap<>();
|
||||
private final Set<URL> loaded = new HashSet<>();
|
||||
|
||||
@Activate
|
||||
public ScriptFileWatcher(final @Reference ScriptEngineManager manager) {
|
||||
public ScriptFileWatcher(final @Reference ScriptEngineManager manager, final @Reference ReadyService readyService) {
|
||||
super(OpenHAB.getConfigFolder() + File.separator + FILE_DIRECTORY);
|
||||
this.manager = manager;
|
||||
this.readyService = readyService;
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Activate
|
||||
@Override
|
||||
public void activate() {
|
||||
super.activate();
|
||||
importResources(new File(pathToWatch));
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduler.scheduleWithFixedDelay(this::checkFiles, INITIAL_DELAY, RECHECK_INTERVAL, TimeUnit.SECONDS);
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
|
||||
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_MODEL)));
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
@Override
|
||||
public void deactivate() {
|
||||
readyService.unregisterTracker(this);
|
||||
ScheduledExecutorService localScheduler = scheduler;
|
||||
if (localScheduler != null) {
|
||||
localScheduler.shutdownNow();
|
||||
@@ -168,7 +176,7 @@ public class ScriptFileWatcher extends AbstractWatchService {
|
||||
|
||||
String scriptType = getScriptType(url);
|
||||
if (scriptType != null) {
|
||||
if (System.currentTimeMillis() < earliestStart) {
|
||||
if (!started) {
|
||||
enqueueUrl(url, scriptType);
|
||||
} else {
|
||||
if (manager.isSupported(scriptType)) {
|
||||
@@ -289,4 +297,17 @@ public class ScriptFileWatcher extends AbstractWatchService {
|
||||
importFile(url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(@NonNull ReadyMarker readyMarker) {
|
||||
started = true;
|
||||
importResources(new File(pathToWatch));
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("scriptwatcher"));
|
||||
scheduler.scheduleWithFixedDelay(this::checkFiles, RECHECK_INTERVAL, RECHECK_INTERVAL, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(@NonNull ReadyMarker readyMarker) {
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
|
||||
+70
-33
@@ -27,7 +27,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -51,6 +50,7 @@ import org.openhab.core.automation.handler.TriggerHandler;
|
||||
import org.openhab.core.automation.handler.TriggerHandlerCallback;
|
||||
import org.openhab.core.automation.internal.TriggerHandlerCallbackImpl.TriggerData;
|
||||
import org.openhab.core.automation.internal.composite.CompositeModuleHandlerFactory;
|
||||
import org.openhab.core.automation.internal.module.handler.SystemTriggerHandler;
|
||||
import org.openhab.core.automation.internal.ruleengine.WrappedAction;
|
||||
import org.openhab.core.automation.internal.ruleengine.WrappedCondition;
|
||||
import org.openhab.core.automation.internal.ruleengine.WrappedModule;
|
||||
@@ -67,14 +67,19 @@ import org.openhab.core.automation.type.ModuleTypeRegistry;
|
||||
import org.openhab.core.automation.type.Output;
|
||||
import org.openhab.core.automation.type.TriggerType;
|
||||
import org.openhab.core.automation.util.ReferenceResolver;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyMarkerFilter;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.openhab.core.storage.Storage;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.ComponentPropertyType;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
@@ -100,17 +105,9 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Markus Rathgeb - use a managed rule
|
||||
* @author Ana Dimova - new reference syntax: list[index], map["key"], bean.field
|
||||
*/
|
||||
@Component(immediate = true)
|
||||
@Component(immediate = true, service = { RuleManager.class })
|
||||
@NonNullByDefault
|
||||
public class RuleEngineImpl implements RuleManager, RegistryChangeListener<ModuleType> {
|
||||
|
||||
@ComponentPropertyType
|
||||
public @interface Config {
|
||||
/**
|
||||
* Delay between rule's re-initialization tries.
|
||||
*/
|
||||
long rule_reinitialization_delay() default 500;
|
||||
}
|
||||
public class RuleEngineImpl implements RuleManager, RegistryChangeListener<ModuleType>, ReadyTracker {
|
||||
|
||||
/**
|
||||
* Constant defining separator between module id and output name.
|
||||
@@ -119,10 +116,7 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
|
||||
private static final String DISABLED_RULE_STORAGE = "automation_rules_disabled";
|
||||
|
||||
/**
|
||||
* Delay between rule's re-initialization tries.
|
||||
*/
|
||||
private final long scheduleReinitializationDelay;
|
||||
private static final ReadyMarker MARKER = new ReadyMarker("ruleengine", "start");
|
||||
|
||||
private final Map<String, WrappedRule> managedRules = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -160,12 +154,15 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
*/
|
||||
private boolean isDisposed = false;
|
||||
|
||||
/**
|
||||
* flag to check whether we have reached a start level where we want to start rule execution
|
||||
*/
|
||||
private boolean started = false;
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(RuleEngineImpl.class);
|
||||
|
||||
/**
|
||||
* A callback that is notified when the status of a {@link Rule} changes.
|
||||
*/
|
||||
private final RuleRegistry ruleRegistry;
|
||||
private final ReadyService readyService;
|
||||
|
||||
/**
|
||||
* {@link Map} holding all Rule context maps. Rule context maps contain dynamic parameters used by the
|
||||
@@ -249,8 +246,9 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
* Constructor of {@link RuleEngineImpl}.
|
||||
*/
|
||||
@Activate
|
||||
public RuleEngineImpl(final Config config, final @Reference ModuleTypeRegistry moduleTypeRegistry,
|
||||
final @Reference RuleRegistry ruleRegistry, final @Reference StorageService storageService) {
|
||||
public RuleEngineImpl(final @Reference ModuleTypeRegistry moduleTypeRegistry,
|
||||
final @Reference RuleRegistry ruleRegistry, final @Reference StorageService storageService,
|
||||
final @Reference ReadyService readyService) {
|
||||
this.contextMap = new HashMap<>();
|
||||
this.moduleHandlerFactories = new HashMap<>(20);
|
||||
|
||||
@@ -263,8 +261,7 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
compositeFactory = new CompositeModuleHandlerFactory(mtRegistry, this);
|
||||
|
||||
this.ruleRegistry = ruleRegistry;
|
||||
|
||||
this.scheduleReinitializationDelay = config.rule_reinitialization_delay();
|
||||
this.readyService = readyService;
|
||||
|
||||
listener = new RegistryChangeListener<Rule>() {
|
||||
@Override
|
||||
@@ -287,6 +284,9 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
for (Rule rule : ruleRegistry.getAll()) {
|
||||
addRule(rule);
|
||||
}
|
||||
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
|
||||
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_RULES)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,12 +507,6 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
f.cancel(true);
|
||||
}
|
||||
}
|
||||
if (scheduleTasks.isEmpty()) {
|
||||
if (executor != null) {
|
||||
executor.shutdown();
|
||||
executor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,7 +755,7 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
.hasNext();) {
|
||||
Map.Entry<String, Set<String>> e = it.next();
|
||||
Set<String> rules = e.getValue();
|
||||
if (rules != null && rules.contains(rUID)) {
|
||||
if (rules.contains(rUID)) {
|
||||
rules.remove(rUID);
|
||||
if (rules.size() < 1) {
|
||||
it.remove();
|
||||
@@ -900,13 +894,13 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
protected void scheduleRuleInitialization(final String rUID) {
|
||||
Future<?> f = scheduleTasks.get(rUID);
|
||||
if (f == null || f.isDone()) {
|
||||
scheduleTasks.put(rUID, getScheduledExecutor().schedule(() -> {
|
||||
scheduleTasks.put(rUID, getScheduledExecutor().submit(() -> {
|
||||
final WrappedRule managedRule = getManagedRule(rUID);
|
||||
if (managedRule == null) {
|
||||
return;
|
||||
}
|
||||
setRule(managedRule);
|
||||
}, scheduleReinitializationDelay, TimeUnit.MILLISECONDS));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,6 +961,10 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
// the rule was unregistered
|
||||
return;
|
||||
}
|
||||
if (!started) {
|
||||
logger.debug("Rule engine not yet started - not executing rule '{}',", ruleUID);
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
final RuleStatus ruleStatus = getRuleStatus(ruleUID);
|
||||
if (ruleStatus != null && ruleStatus != RuleStatus.IDLE) {
|
||||
@@ -1213,7 +1211,8 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
if (currentExecutor != null && !currentExecutor.isShutdown()) {
|
||||
return currentExecutor;
|
||||
}
|
||||
final ScheduledExecutorService newExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
final ScheduledExecutorService newExecutor = Executors
|
||||
.newSingleThreadScheduledExecutor(new NamedThreadFactory("ruleengine"));
|
||||
executor = newExecutor;
|
||||
return newExecutor;
|
||||
}
|
||||
@@ -1422,4 +1421,42 @@ public class RuleEngineImpl implements RuleManager, RegistryChangeListener<Modul
|
||||
return outputName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
|
||||
executeRulesWithStartLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
|
||||
started = false;
|
||||
}
|
||||
|
||||
private void executeRulesWithStartLevel() {
|
||||
getScheduledExecutor().submit(() -> {
|
||||
ruleRegistry.getAll().stream() //
|
||||
.filter(r -> mustTrigger(r)) //
|
||||
.forEach(r -> runNow(r.getUID(), true,
|
||||
Map.of(SystemTriggerHandler.OUT_STARTLEVEL, StartLevelService.STARTLEVEL_RULES)));
|
||||
started = true;
|
||||
readyService.markReady(MARKER);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean mustTrigger(Rule r) {
|
||||
for (Trigger t : r.getTriggers()) {
|
||||
if (t.getTypeUID() == SystemTriggerHandler.STARTLEVEL_MODULE_TYPE_ID) {
|
||||
Object slObj = t.getConfiguration().get(SystemTriggerHandler.CFG_STARTLEVEL);
|
||||
try {
|
||||
Integer sl = Integer.valueOf(slObj.toString());
|
||||
if (sl < StartLevelService.STARTLEVEL_RULEENGINE) {
|
||||
return true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("Configuration '{}' is not a valid start level!", slObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -46,6 +46,7 @@ import org.openhab.core.config.core.ConfigDescriptionParameter;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
@@ -154,6 +155,17 @@ public class RuleRegistryImpl extends AbstractRegistry<Rule, String, RuleProvide
|
||||
super.unsetEventPublisher(eventPublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Reference
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
super.setReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
super.unsetReadyService(readyService);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, name = "ManagedRuleProvider")
|
||||
protected void setManagedProvider(ManagedRuleProvider managedProvider) {
|
||||
super.setManagedProvider(managedProvider);
|
||||
|
||||
+2
-3
@@ -27,9 +27,8 @@ import org.openhab.core.common.NamedThreadFactory;
|
||||
|
||||
/**
|
||||
* This class is implementation of {@link TriggerHandlerCallback} used by the {@link Trigger}s to notify rule engine
|
||||
* about
|
||||
* appearing of new triggered data. There is one and only one {@link TriggerHandlerCallback} per RuleImpl and it is used
|
||||
* by all rule's {@link Trigger}s.
|
||||
* about appearing of new triggered data. There is one and only one {@link TriggerHandlerCallback} per Rule and
|
||||
* it is used by all rule's {@link Trigger}s.
|
||||
*
|
||||
* @author Yordan Mihaylov - Initial contribution
|
||||
* @author Kai Kreuzer - improved stability
|
||||
|
||||
+24
-8
@@ -29,6 +29,7 @@ import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventFilter;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.events.system.StartlevelEvent;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
@@ -52,6 +53,8 @@ public class SystemTriggerHandler extends BaseTriggerModuleHandler implements Ev
|
||||
private final Set<String> types;
|
||||
private final BundleContext bundleContext;
|
||||
|
||||
private boolean triggered = false;
|
||||
|
||||
private ServiceRegistration<?> eventSubscriberRegistration;
|
||||
|
||||
public SystemTriggerHandler(Trigger module, BundleContext bundleContext) {
|
||||
@@ -82,20 +85,20 @@ public class SystemTriggerHandler extends BaseTriggerModuleHandler implements Ev
|
||||
|
||||
@Override
|
||||
public void receive(Event event) {
|
||||
final ModuleHandlerCallback callback = this.callback;
|
||||
if (!(callback instanceof TriggerHandlerCallback)) {
|
||||
if (triggered) {
|
||||
// this trigger only works once
|
||||
return;
|
||||
}
|
||||
|
||||
TriggerHandlerCallback thCallback = (TriggerHandlerCallback) callback;
|
||||
logger.trace("Received Event: Source: {} Topic: {} Type: {} Payload: {}", event.getSource(), event.getTopic(),
|
||||
event.getType(), event.getPayload());
|
||||
Map<String, Object> values = new HashMap<>();
|
||||
if (event instanceof StartlevelEvent && STARTLEVEL_MODULE_TYPE_ID.equals(module.getTypeUID())) {
|
||||
Integer sl = ((StartlevelEvent) event).getStartlevel();
|
||||
if (startlevel.equals(sl)) {
|
||||
values.put(OUT_STARTLEVEL, sl);
|
||||
thCallback.triggered(module, values);
|
||||
if (startlevel <= sl) {
|
||||
if (sl > StartLevelService.STARTLEVEL_RULEENGINE) {
|
||||
// only execute rules if their start level is higher than the rule engine activation level, since
|
||||
// otherwise the rule engine takes care of the execution already
|
||||
trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,4 +116,17 @@ public class SystemTriggerHandler extends BaseTriggerModuleHandler implements Ev
|
||||
public boolean apply(Event event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void trigger() {
|
||||
final ModuleHandlerCallback callback = this.callback;
|
||||
if (!(callback instanceof TriggerHandlerCallback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TriggerHandlerCallback thCallback = (TriggerHandlerCallback) callback;
|
||||
Map<String, Object> values = new HashMap<>();
|
||||
values.put(OUT_STARTLEVEL, startlevel);
|
||||
thCallback.triggered(module, values);
|
||||
triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
+38
-3
@@ -16,10 +16,19 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventFilter;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyMarkerFilter;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -28,11 +37,25 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@Component
|
||||
public class EventLogger implements EventSubscriber {
|
||||
public class EventLogger implements EventSubscriber, ReadyTracker {
|
||||
|
||||
private final Map<String, Logger> eventLoggers = new HashMap<>();
|
||||
|
||||
private final Set<String> subscribedEventTypes = Set.of(EventSubscriber.ALL_EVENT_TYPES);
|
||||
private final ReadyService readyService;
|
||||
|
||||
private boolean loggingActive = false;
|
||||
|
||||
@Activate
|
||||
public EventLogger(@Reference ReadyService readyService) {
|
||||
this.readyService = readyService;
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
|
||||
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_RULES)));
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
readyService.unregisterTracker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSubscribedEventTypes() {
|
||||
@@ -49,7 +72,9 @@ public class EventLogger implements EventSubscriber {
|
||||
Logger logger = getLogger(event.getType());
|
||||
logger.trace("Received event of type '{}' under the topic '{}' with payload: '{}'", event.getType(),
|
||||
event.getTopic(), event.getPayload());
|
||||
logger.info("{}", event);
|
||||
if (loggingActive) {
|
||||
logger.info("{}", event);
|
||||
}
|
||||
}
|
||||
|
||||
private Logger getLogger(String eventType) {
|
||||
@@ -61,4 +86,14 @@ public class EventLogger implements EventSubscriber {
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(@NonNull ReadyMarker readyMarker) {
|
||||
loggingActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(@NonNull ReadyMarker readyMarker) {
|
||||
loggingActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,34 +12,30 @@
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src-gen">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="model">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src-gen"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="annotationpath" value="target/dependency"/>
|
||||
<attribute name="module" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="annotationpath" value="target/dependency"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="notestsources">
|
||||
<attributes>
|
||||
<attribute name="test" value="true"/>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
||||
+17
-29
@@ -14,12 +14,10 @@ package org.openhab.core.model.rule.runtime.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -81,9 +79,6 @@ import org.slf4j.LoggerFactory;
|
||||
public class DSLRuleProvider
|
||||
implements RuleProvider, ModelRepositoryChangeListener, DSLScriptContextProvider, ReadyTracker {
|
||||
|
||||
private static final String RULES_MODEL_NAME = "rules";
|
||||
private static final String ITEMS_MODEL_NAME = "items";
|
||||
private static final String THINGS_MODEL_NAME = "things";
|
||||
static final String MIMETYPE_OPENHAB_DSL_RULE = "application/vnd.openhab.dsl.rule";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DSLRuleProvider.class);
|
||||
@@ -91,8 +86,8 @@ public class DSLRuleProvider
|
||||
private final Map<String, Rule> rules = new ConcurrentHashMap<>();
|
||||
private final Map<String, IEvaluationContext> contexts = new ConcurrentHashMap<>();
|
||||
private final Map<String, XExpression> xExpressions = new ConcurrentHashMap<>();
|
||||
private final ReadyMarker marker = new ReadyMarker("rules", "dslprovider");
|
||||
private int triggerId = 0;
|
||||
private Set<String> markers = new HashSet<>();
|
||||
|
||||
private final ModelRepository modelRepository;
|
||||
private final ReadyService readyService;
|
||||
@@ -105,7 +100,8 @@ public class DSLRuleProvider
|
||||
|
||||
@Activate
|
||||
protected void activate() {
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType("dsl"));
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(RulesRefresher.RULES_REFRESH_MARKER_TYPE)
|
||||
.withIdentifier(RulesRefresher.RULES_REFRESH));
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
@@ -114,7 +110,6 @@ public class DSLRuleProvider
|
||||
rules.clear();
|
||||
contexts.clear();
|
||||
xExpressions.clear();
|
||||
markers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -400,35 +395,28 @@ public class DSLRuleProvider
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReady() {
|
||||
return markers.containsAll(
|
||||
Set.of(ITEMS_MODEL_NAME, THINGS_MODEL_NAME, RULES_MODEL_NAME, RulesRefresher.RULES_REFRESH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
|
||||
markers.add(readyMarker.getIdentifier());
|
||||
if (isReady()) {
|
||||
for (String ruleFileName : modelRepository.getAllModelNamesOfType(RULES_MODEL_NAME)) {
|
||||
EObject model = modelRepository.getModel(ruleFileName);
|
||||
String ruleModelName = ruleFileName.substring(0, ruleFileName.indexOf("."));
|
||||
if (model instanceof RuleModel) {
|
||||
RuleModel ruleModel = (RuleModel) model;
|
||||
int index = 1;
|
||||
for (org.openhab.core.model.rule.rules.Rule rule : ruleModel.getRules()) {
|
||||
addRule(toRule(ruleModelName, rule, index));
|
||||
xExpressions.put(ruleModelName + "-" + index, rule.getScript());
|
||||
index++;
|
||||
}
|
||||
handleVarDeclarations(ruleModelName, ruleModel);
|
||||
for (String ruleFileName : modelRepository.getAllModelNamesOfType("rules")) {
|
||||
EObject model = modelRepository.getModel(ruleFileName);
|
||||
String ruleModelName = ruleFileName.substring(0, ruleFileName.indexOf("."));
|
||||
if (model instanceof RuleModel) {
|
||||
RuleModel ruleModel = (RuleModel) model;
|
||||
int index = 1;
|
||||
for (org.openhab.core.model.rule.rules.Rule rule : ruleModel.getRules()) {
|
||||
addRule(toRule(ruleModelName, rule, index));
|
||||
xExpressions.put(ruleModelName + "-" + index, rule.getScript());
|
||||
index++;
|
||||
}
|
||||
handleVarDeclarations(ruleModelName, ruleModel);
|
||||
}
|
||||
modelRepository.addModelRepositoryChangeListener(this);
|
||||
}
|
||||
modelRepository.addModelRepositoryChangeListener(this);
|
||||
readyService.markReady(marker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
|
||||
markers.remove(readyMarker.getIdentifier());
|
||||
readyService.unmarkReady(marker);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-35
@@ -21,9 +21,6 @@ import java.util.concurrent.TimeUnit;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.events.system.StartlevelEvent;
|
||||
import org.openhab.core.events.system.SystemEventFactory;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.items.ItemRegistryChangeListener;
|
||||
@@ -33,12 +30,14 @@ import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyMarkerFilter;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingRegistryChangeListener;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
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.ReferenceCardinality;
|
||||
import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
@@ -59,7 +58,8 @@ public class RulesRefresher implements ReadyTracker {
|
||||
// delay in seconds before rule resources are refreshed after items or services have changed
|
||||
private static final long REFRESH_DELAY = 5;
|
||||
|
||||
public static final String RULES_REFRESH = "rules_refresh";
|
||||
public static final String RULES_REFRESH_MARKER_TYPE = "rules";
|
||||
public static final String RULES_REFRESH = "refresh";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RulesRefresher.class);
|
||||
|
||||
@@ -67,25 +67,24 @@ public class RulesRefresher implements ReadyTracker {
|
||||
private final ScheduledExecutorService scheduler = Executors
|
||||
.newSingleThreadScheduledExecutor(new NamedThreadFactory("rulesRefresher"));
|
||||
private boolean started;
|
||||
private final ReadyMarker marker = new ReadyMarker("dsl", RULES_REFRESH);
|
||||
private final ReadyMarker marker = new ReadyMarker("rules", RULES_REFRESH);
|
||||
|
||||
private final ModelRepository modelRepository;
|
||||
private final ItemRegistry itemRegistry;
|
||||
private final ThingRegistry thingRegistry;
|
||||
private final EventPublisher eventPublisher;
|
||||
private final ReadyService readyService;
|
||||
|
||||
private final ItemRegistryChangeListener itemRegistryChangeListener = new ItemRegistryChangeListener() {
|
||||
@Override
|
||||
public void added(Item element) {
|
||||
logger.debug("Item \"{}\" added => rules are going to be refreshed", element.getName());
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(Item element) {
|
||||
logger.debug("Item \"{}\" removed => rules are going to be refreshed", element.getName());
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +94,7 @@ public class RulesRefresher implements ReadyTracker {
|
||||
@Override
|
||||
public void allItemsChanged(Collection<String> oldItemNames) {
|
||||
logger.debug("All items changed => rules are going to be refreshed");
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,13 +102,13 @@ public class RulesRefresher implements ReadyTracker {
|
||||
@Override
|
||||
public void added(Thing element) {
|
||||
logger.debug("Thing \"{}\" added => rules are going to be refreshed", element.getUID().getAsString());
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(Thing element) {
|
||||
logger.debug("Thing \"{}\" removed => rules are going to be refreshed", element.getUID().getAsString());
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,32 +118,36 @@ public class RulesRefresher implements ReadyTracker {
|
||||
|
||||
@Activate
|
||||
public RulesRefresher(@Reference ModelRepository modelRepository, @Reference ItemRegistry itemRegistry,
|
||||
@Reference ThingRegistry thingRegistry, @Reference EventPublisher eventPublisher,
|
||||
@Reference ReadyService readyService) {
|
||||
@Reference ThingRegistry thingRegistry, @Reference ReadyService readyService) {
|
||||
this.modelRepository = modelRepository;
|
||||
this.itemRegistry = itemRegistry;
|
||||
this.thingRegistry = thingRegistry;
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.readyService = readyService;
|
||||
}
|
||||
|
||||
@Activate
|
||||
protected void activate() {
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType("dsl").withIdentifier("rules"));
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
|
||||
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_MODEL)));
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
readyService.unregisterTracker(this);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void addActionService(ActionService actionService) {
|
||||
if (started) {
|
||||
logger.debug("Script action added => rules are going to be refreshed");
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeActionService(ActionService actionService) {
|
||||
if (started) {
|
||||
logger.debug("Script action removed => rules are going to be refreshed");
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,18 +155,18 @@ public class RulesRefresher implements ReadyTracker {
|
||||
protected void addThingActions(ThingActions thingActions) {
|
||||
if (started) {
|
||||
logger.debug("Thing automation action added => rules are going to be refreshed");
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeThingActions(ThingActions thingActions) {
|
||||
if (started) {
|
||||
logger.debug("Thing automation action removed => rules are going to be refreshed");
|
||||
scheduleRuleRefresh();
|
||||
scheduleRuleRefresh(REFRESH_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void scheduleRuleRefresh() {
|
||||
protected synchronized void scheduleRuleRefresh(long delay) {
|
||||
ScheduledFuture<?> localJob = job;
|
||||
if (localJob != null && !localJob.isDone()) {
|
||||
localJob.cancel(false);
|
||||
@@ -174,30 +177,23 @@ public class RulesRefresher implements ReadyTracker {
|
||||
} catch (Exception e) {
|
||||
logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
|
||||
}
|
||||
readyService.markReady(marker);
|
||||
}, REFRESH_DELAY, TimeUnit.SECONDS);
|
||||
readyService.unmarkReady(marker);
|
||||
}
|
||||
|
||||
private void setStartLevel() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
// TODO: This is still a very dirty hack in the absence of a proper system start level management.
|
||||
scheduler.schedule(() -> {
|
||||
StartlevelEvent startlevelEvent = SystemEventFactory.createStartlevelEvent(20);
|
||||
eventPublisher.post(startlevelEvent);
|
||||
}, 15, TimeUnit.SECONDS);
|
||||
}
|
||||
if (!started) {
|
||||
started = true;
|
||||
readyService.markReady(marker);
|
||||
}
|
||||
}, delay, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
|
||||
scheduleRuleRefresh(0);
|
||||
itemRegistry.addRegistryChangeListener(itemRegistryChangeListener);
|
||||
thingRegistry.addRegistryChangeListener(thingRegistryChangeListener);
|
||||
setStartLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
|
||||
itemRegistry.removeRegistryChangeListener(itemRegistryChangeListener);
|
||||
thingRegistry.removeRegistryChangeListener(thingRegistryChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-7
@@ -22,9 +22,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.common.SafeCaller;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
@@ -48,6 +51,11 @@ import org.openhab.core.persistence.strategy.PersistenceCronStrategy;
|
||||
import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
import org.openhab.core.scheduler.CronScheduler;
|
||||
import org.openhab.core.scheduler.ScheduledCompletableFuture;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyMarkerFilter;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.openhab.core.service.StartLevelService;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
@@ -67,14 +75,18 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@Component(immediate = true, service = PersistenceManager.class)
|
||||
@NonNullByDefault
|
||||
public class PersistenceManagerImpl implements ItemRegistryChangeListener, PersistenceManager, StateChangeListener {
|
||||
public class PersistenceManagerImpl
|
||||
implements ItemRegistryChangeListener, PersistenceManager, StateChangeListener, ReadyTracker {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PersistenceManagerImpl.class);
|
||||
|
||||
private final ReadyMarker marker = new ReadyMarker("persistence", "restore");
|
||||
|
||||
// the scheduler used for timer events
|
||||
private final CronScheduler scheduler;
|
||||
private final ItemRegistry itemRegistry;
|
||||
private final SafeCaller safeCaller;
|
||||
private final ReadyService readyService;
|
||||
private volatile boolean started = false;
|
||||
|
||||
final Map<String, PersistenceService> persistenceServices = new HashMap<>();
|
||||
@@ -83,17 +95,17 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, Persi
|
||||
|
||||
@Activate
|
||||
public PersistenceManagerImpl(final @Reference CronScheduler scheduler, final @Reference ItemRegistry itemRegistry,
|
||||
final @Reference SafeCaller safeCaller) {
|
||||
final @Reference SafeCaller safeCaller, final @Reference ReadyService readyService) {
|
||||
this.scheduler = scheduler;
|
||||
this.itemRegistry = itemRegistry;
|
||||
this.safeCaller = safeCaller;
|
||||
this.readyService = readyService;
|
||||
}
|
||||
|
||||
@Activate
|
||||
protected void activate() {
|
||||
allItemsChanged(Collections.emptySet());
|
||||
started = true;
|
||||
itemRegistry.addRegistryChangeListener(this);
|
||||
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
|
||||
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_MODEL)));
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
@@ -373,11 +385,11 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, Persi
|
||||
@Override
|
||||
public void addConfig(final String dbId, final PersistenceServiceConfiguration config) {
|
||||
synchronized (persistenceServiceConfigs) {
|
||||
if (persistenceServiceConfigs.containsKey(dbId)) {
|
||||
if (started && persistenceServiceConfigs.containsKey(dbId)) {
|
||||
stopEventHandling(dbId);
|
||||
}
|
||||
persistenceServiceConfigs.put(dbId, config);
|
||||
if (persistenceServices.containsKey(dbId)) {
|
||||
if (started && persistenceServices.containsKey(dbId)) {
|
||||
startEventHandling(dbId);
|
||||
}
|
||||
}
|
||||
@@ -465,4 +477,24 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, Persi
|
||||
public void stateUpdated(Item item, State state) {
|
||||
handleStateEvent(item, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
|
||||
ExecutorService scheduler = Executors.newSingleThreadExecutor(new NamedThreadFactory("persistenceManager"));
|
||||
scheduler.submit(() -> {
|
||||
allItemsChanged(Collections.emptySet());
|
||||
for (String dbId : persistenceServices.keySet()) {
|
||||
startEventHandling(dbId);
|
||||
}
|
||||
started = true;
|
||||
readyService.markReady(marker);
|
||||
itemRegistry.addRegistryChangeListener(this);
|
||||
});
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
|
||||
readyService.unmarkReady(marker);
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.registry.AbstractRegistry;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@@ -282,6 +283,17 @@ public class ThingRegistryImpl extends AbstractRegistry<Thing, ThingUID, ThingPr
|
||||
super.unsetEventPublisher(eventPublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Reference
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
super.setReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
super.unsetReadyService(readyService);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, name = "ManagedThingProvider")
|
||||
protected void setManagedProvider(ManagedThingProvider provider) {
|
||||
super.setManagedProvider(provider);
|
||||
|
||||
+12
@@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
@@ -110,6 +111,17 @@ public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLin
|
||||
super.setEventPublisher(eventPublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Reference
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
super.setReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
super.unsetReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetEventPublisher(final EventPublisher eventPublisher) {
|
||||
super.unsetEventPublisher(eventPublisher);
|
||||
|
||||
+16
@@ -32,6 +32,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
@@ -82,6 +84,7 @@ public abstract class AbstractRegistry<@NonNull E extends Identifiable<K>, @NonN
|
||||
private Optional<ManagedProvider<E, K>> managedProvider = Optional.empty();
|
||||
|
||||
private @Nullable EventPublisher eventPublisher;
|
||||
private @Nullable ReadyService readyService;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -427,6 +430,11 @@ public abstract class AbstractRegistry<@NonNull E extends Identifiable<K>, @NonN
|
||||
elementWriteLock.unlock();
|
||||
}
|
||||
elementsAdded.forEach(this::notifyListenersAboutAddedElement);
|
||||
|
||||
if (provider instanceof ManagedProvider && readyService != null) {
|
||||
readyService.markReady(
|
||||
new ReadyMarker("managed", providerClazz.getSimpleName().replace("Provider", "").toLowerCase()));
|
||||
}
|
||||
logger.debug("Provider \"{}\" has been added.", provider.getClass().getName());
|
||||
}
|
||||
|
||||
@@ -654,6 +662,14 @@ public abstract class AbstractRegistry<@NonNull E extends Identifiable<K>, @NonN
|
||||
this.eventPublisher = null;
|
||||
}
|
||||
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
this.readyService = readyService;
|
||||
}
|
||||
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
this.readyService = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used in a subclass in order to post events through the openHAB events bus. A common
|
||||
* use case is to notify event subscribers about an element which has been added/removed/updated to the registry.
|
||||
|
||||
+12
@@ -39,6 +39,7 @@ import org.openhab.core.items.MetadataRegistry;
|
||||
import org.openhab.core.items.RegistryHook;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.service.CommandDescriptionService;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.service.StateDescriptionService;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
@@ -280,6 +281,17 @@ public class ItemRegistryImpl extends AbstractRegistry<Item, String, ItemProvide
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Reference
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
super.setReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
super.unsetReadyService(readyService);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void setUnitProvider(UnitProvider unitProvider) {
|
||||
this.unitProvider = unitProvider;
|
||||
|
||||
+12
@@ -20,6 +20,7 @@ import org.openhab.core.items.Metadata;
|
||||
import org.openhab.core.items.MetadataKey;
|
||||
import org.openhab.core.items.MetadataProvider;
|
||||
import org.openhab.core.items.MetadataRegistry;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
@@ -71,6 +72,17 @@ public class MetadataRegistryImpl extends AbstractRegistry<Metadata, MetadataKey
|
||||
super.unsetEventPublisher(eventPublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Reference
|
||||
protected void setReadyService(ReadyService readyService) {
|
||||
super.setReadyService(readyService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsetReadyService(ReadyService readyService) {
|
||||
super.unsetReadyService(readyService);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void setManagedProvider(ManagedMetadataProvider provider) {
|
||||
super.setManagedProvider(provider);
|
||||
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.service;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.events.system.StartlevelEvent;
|
||||
import org.openhab.core.events.system.SystemEventFactory;
|
||||
import org.openhab.core.service.ReadyService.ReadyTracker;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.startlevel.FrameworkStartLevel;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This service combines different {@link ReadyMarker}s into a new start level ready marker and thus
|
||||
* lets other services depend on those, without having to know about the single markers.
|
||||
* This brings an important decoupling, since the set of markers for a certain start level might
|
||||
* depend on the individual set up-
|
||||
* The start level service is therefore configurable, so that users have a chance to adapt the
|
||||
* conditions upon a certain start level is reached.
|
||||
*
|
||||
* Start levels are defined as values between 0 and 100. They carry the following semantics:
|
||||
*
|
||||
* 00 - OSGi framework has been started.
|
||||
* 10 - OSGi application start level has been reached, i.e. bundles are activated.
|
||||
* 20 - Model entities (items, things, links, persist config) have been loaded, both from db as well as files.
|
||||
* 30 - Item states have been restored from persistence service, where applicable.
|
||||
* 40 - Rules are loaded and parsed, both from db as well as dsl and script files.
|
||||
* 50 - Rule engine has executed all "system started" rules and is active.
|
||||
* 70 - User interface is up and running.
|
||||
* 80 - All things have been initialized.
|
||||
* 100 - Startup is fully complete.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true, configurationPid = "org.openhab.startlevel")
|
||||
public class StartLevelService {
|
||||
|
||||
public final static String STARTLEVEL_MARKER_TYPE = "startlevel";
|
||||
|
||||
public final static int STARTLEVEL_OSGI = 10;
|
||||
public final static int STARTLEVEL_MODEL = 20;
|
||||
public final static int STARTLEVEL_STATES = 30;
|
||||
public final static int STARTLEVEL_RULES = 40;
|
||||
public final static int STARTLEVEL_RULEENGINE = 50;
|
||||
public final static int STARTLEVEL_UI = 70;
|
||||
public final static int STARTLEVEL_THINGS = 80;
|
||||
public final static int STARTLEVEL_COMPLETE = 100;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(StartLevelService.class);
|
||||
|
||||
private final BundleContext bundleContext;
|
||||
private final ReadyService readyService;
|
||||
private final EventPublisher eventPublisher;
|
||||
|
||||
private final Set<ReadyMarker> markers = ConcurrentHashMap.newKeySet();
|
||||
private final Map<String, ReadyTracker> trackers = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, @NonNull ReadyMarker> slmarker = new ConcurrentHashMap<>();
|
||||
|
||||
private @Nullable ScheduledFuture<?> job;
|
||||
private final ScheduledExecutorService scheduler = Executors
|
||||
.newSingleThreadScheduledExecutor(new NamedThreadFactory("startlevel"));
|
||||
|
||||
private int openHABStartLevel = 0;
|
||||
|
||||
private Map<Integer, Set<ReadyMarker>> startlevels = Map.of();
|
||||
|
||||
@Activate
|
||||
public StartLevelService(BundleContext bundleContext, @Reference ReadyService readyService,
|
||||
@Reference EventPublisher eventPublisher) {
|
||||
this.bundleContext = bundleContext;
|
||||
this.readyService = readyService;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@Activate
|
||||
protected void activate(Map<String, Object> configuration) {
|
||||
modified(configuration);
|
||||
|
||||
job = scheduler.scheduleWithFixedDelay(() -> {
|
||||
handleOSGiStartlevel();
|
||||
|
||||
if (openHABStartLevel >= 10) {
|
||||
for (Integer level : new TreeSet<>(startlevels.keySet())) {
|
||||
if (openHABStartLevel >= level) {
|
||||
continue;
|
||||
} else {
|
||||
boolean reached = isStartLevelReached(startlevels.get(level));
|
||||
if (reached) {
|
||||
setStartLevel(level);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (openHABStartLevel < 100) {
|
||||
setStartLevel(100);
|
||||
}
|
||||
}
|
||||
}, 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current start level of openHAB
|
||||
*
|
||||
* @return the current start level
|
||||
*/
|
||||
public int getStartLevel() {
|
||||
return openHABStartLevel;
|
||||
}
|
||||
|
||||
private boolean isStartLevelReached(@Nullable Set<ReadyMarker> markerSet) {
|
||||
if (markerSet == null) {
|
||||
return true;
|
||||
}
|
||||
for (ReadyMarker m : markerSet) {
|
||||
if (!markers.contains(m)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleOSGiStartlevel() {
|
||||
FrameworkStartLevel sl = this.bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
|
||||
int defaultStartLevel = sl.getInitialBundleStartLevel();
|
||||
int startLevel = sl.getStartLevel();
|
||||
if (startLevel >= defaultStartLevel && openHABStartLevel < 10) {
|
||||
setStartLevel(10);
|
||||
} else if (startLevel < defaultStartLevel && openHABStartLevel >= 10) {
|
||||
setStartLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(Map<String, Object> configuration) {
|
||||
// clean up
|
||||
slmarker.clear();
|
||||
trackers.values().forEach(t -> readyService.unregisterTracker(t));
|
||||
|
||||
// set up trackers and markers
|
||||
startlevels = parseConfig(configuration);
|
||||
startlevels.keySet()
|
||||
.forEach(sl -> slmarker.put(sl, new ReadyMarker(STARTLEVEL_MARKER_TYPE, Integer.toString(sl))));
|
||||
startlevels.values().stream().forEach(ms -> ms.forEach(e -> registerTracker(e)));
|
||||
}
|
||||
|
||||
private void registerTracker(ReadyMarker e) {
|
||||
String type = e.getType();
|
||||
if (!trackers.containsKey(type)) {
|
||||
ReadyTracker tracker = new ReadyTracker() {
|
||||
@Override
|
||||
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
|
||||
markers.remove(readyMarker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
|
||||
markers.add(readyMarker);
|
||||
}
|
||||
};
|
||||
readyService.registerTracker(tracker, new ReadyMarkerFilter().withType(type));
|
||||
trackers.put(type, tracker);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, Set<ReadyMarker>> parseConfig(Map<String, Object> configuration) {
|
||||
return configuration.entrySet().stream() //
|
||||
.filter(e -> hasIntegerKey(e)) //
|
||||
.map(e -> new AbstractMap.SimpleEntry<>(Integer.valueOf(e.getKey()), markerSet(e.getValue()))) //
|
||||
.sorted(Map.Entry.<Integer, Set<ReadyMarker>> comparingByKey().reversed()) //
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private Set<ReadyMarker> markerSet(Object value) {
|
||||
Set<ReadyMarker> markerSet = new HashSet<>();
|
||||
if (value instanceof String) {
|
||||
String[] segments = ((String) value).split(",");
|
||||
for (String segment : segments) {
|
||||
if (segment.contains(":")) {
|
||||
String[] markerParts = segment.strip().split(":");
|
||||
markerSet.add(new ReadyMarker(markerParts[0], markerParts[1]));
|
||||
} else {
|
||||
logger.warn("Ignoring invalid configuration value '{}'", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return markerSet;
|
||||
}
|
||||
|
||||
private boolean hasIntegerKey(Entry<String, Object> entry) {
|
||||
try {
|
||||
Integer.valueOf(entry.getKey());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
slmarker.clear();
|
||||
trackers.values().forEach(t -> readyService.unregisterTracker(t));
|
||||
ScheduledFuture<?> job = this.job;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStartLevel(int level) {
|
||||
ReadyMarker marker = slmarker.get(level);
|
||||
if (marker != null) {
|
||||
readyService.markReady(marker);
|
||||
}
|
||||
openHABStartLevel = level;
|
||||
scheduler.submit(() -> {
|
||||
StartlevelEvent startlevelEvent = SystemEventFactory.createStartlevelEvent(level);
|
||||
eventPublisher.post(startlevelEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
-include: ../itest-common.bndrun
|
||||
|
||||
Bundle-SymbolicName: ${project.artifactId}
|
||||
Fragment-Host: org.openhab.core.automation
|
||||
|
||||
-runrequires: bnd.identity;id='org.openhab.core.automation.integration.tests'
|
||||
|
||||
|
||||
+6
@@ -36,6 +36,7 @@ import org.openhab.core.automation.RuleStatus;
|
||||
import org.openhab.core.automation.RuleStatusInfo;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.events.RuleStatusInfoEvent;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.type.ActionType;
|
||||
import org.openhab.core.automation.type.Input;
|
||||
import org.openhab.core.automation.type.ModuleTypeRegistry;
|
||||
@@ -54,6 +55,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
@@ -165,6 +167,10 @@ public class AutomationIntegrationJsonTest extends JavaOSGiTest {
|
||||
assertThat(managedRuleProvider, is(notNullValue()));
|
||||
assertThat(moduleTypeRegistry, is(notNullValue()));
|
||||
}, 9000, 1000);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) ruleManager).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
|
||||
logger.info("@Before.finish");
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -48,6 +48,7 @@ import org.openhab.core.automation.events.RuleAddedEvent;
|
||||
import org.openhab.core.automation.events.RuleRemovedEvent;
|
||||
import org.openhab.core.automation.events.RuleStatusInfoEvent;
|
||||
import org.openhab.core.automation.events.RuleUpdatedEvent;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.template.RuleTemplate;
|
||||
import org.openhab.core.automation.template.RuleTemplateProvider;
|
||||
import org.openhab.core.automation.template.Template;
|
||||
@@ -75,6 +76,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.slf4j.Logger;
|
||||
@@ -164,6 +166,10 @@ public class AutomationIntegrationTest extends JavaOSGiTest {
|
||||
assertThat(templateRegistry, is(notNullValue()));
|
||||
assertThat(managedRuleProvider, is(notNullValue()));
|
||||
}, 9000, 1000);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) ruleEngine).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
|
||||
logger.info("@Before.finish");
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -31,6 +31,7 @@ import org.openhab.core.automation.Rule;
|
||||
import org.openhab.core.automation.RuleManager;
|
||||
import org.openhab.core.automation.RuleRegistry;
|
||||
import org.openhab.core.automation.RuleStatus;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
import org.openhab.core.automation.util.RuleBuilder;
|
||||
import org.openhab.core.common.registry.ProviderChangeListener;
|
||||
@@ -47,6 +48,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -82,6 +84,9 @@ public class RunRuleModuleTest extends JavaOSGiTest {
|
||||
}
|
||||
});
|
||||
registerService(volatileStorageService);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
}
|
||||
|
||||
private Rule createSceneRule() {
|
||||
|
||||
+5
@@ -36,6 +36,7 @@ import org.openhab.core.automation.RuleRegistry;
|
||||
import org.openhab.core.automation.RuleStatus;
|
||||
import org.openhab.core.automation.RuleStatusDetail;
|
||||
import org.openhab.core.automation.events.RuleStatusInfoEvent;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.internal.module.handler.CompareConditionHandler;
|
||||
import org.openhab.core.automation.type.ModuleTypeRegistry;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
@@ -54,6 +55,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
import org.openhab.core.types.TypeParser;
|
||||
@@ -94,6 +96,9 @@ public class RuntimeRuleTest extends JavaOSGiTest {
|
||||
}
|
||||
});
|
||||
registerService(volatileStorageService);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -49,4 +49,54 @@ Fragment-Host: org.openhab.core.automation.module.script
|
||||
org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\
|
||||
org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\
|
||||
jollyday;version='[0.5.10,0.5.11)',\
|
||||
org.threeten.extra;version='[1.5.0,1.5.1)'
|
||||
org.threeten.extra;version='[1.5.0,1.5.1)',\
|
||||
com.google.guava;version='[27.1.0,27.1.1)',\
|
||||
com.google.guava.failureaccess;version='[1.0.1,1.0.2)',\
|
||||
com.google.inject;version='[3.0.0,3.0.1)',\
|
||||
io.github.classgraph;version='[4.8.35,4.8.36)',\
|
||||
log4j;version='[1.2.17,1.2.18)',\
|
||||
org.antlr.runtime;version='[3.2.0,3.2.1)',\
|
||||
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
|
||||
org.apache.xbean.bundleutils;version='[4.17.0,4.17.1)',\
|
||||
org.apache.xbean.finder;version='[4.17.0,4.17.1)',\
|
||||
org.eclipse.emf.common;version='[2.17.0,2.17.1)',\
|
||||
org.eclipse.emf.ecore;version='[2.20.0,2.20.1)',\
|
||||
org.eclipse.emf.ecore.xmi;version='[2.16.0,2.16.1)',\
|
||||
org.eclipse.equinox.common;version='[3.12.0,3.12.1)',\
|
||||
org.eclipse.jetty.client;version='[9.4.20,9.4.21)',\
|
||||
org.eclipse.jetty.websocket.api;version='[9.4.20,9.4.21)',\
|
||||
org.eclipse.jetty.websocket.client;version='[9.4.20,9.4.21)',\
|
||||
org.eclipse.jetty.websocket.common;version='[9.4.20,9.4.21)',\
|
||||
org.eclipse.jetty.xml;version='[9.4.20,9.4.21)',\
|
||||
org.eclipse.xtend.lib;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtend.lib.macro;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtext;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtext.common.types;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtext.util;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtext.xbase;version='[2.23.0,2.23.1)',\
|
||||
org.eclipse.xtext.xbase.lib;version='[2.23.0,2.23.1)',\
|
||||
org.glassfish.hk2.external.aopalliance-repackaged;version='[2.4.0,2.4.1)',\
|
||||
org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\
|
||||
org.objectweb.asm;version='[8.0.1,8.0.2)',\
|
||||
org.objectweb.asm.commons;version='[8.0.1,8.0.2)',\
|
||||
org.objectweb.asm.tree;version='[8.0.1,8.0.2)',\
|
||||
org.openhab.core.audio;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.io.http;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.io.net;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.core;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.item;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.persistence;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.rule;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.script;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.script.runtime;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.sitemap;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.thing;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.model.thing.runtime;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.persistence;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.transform;version='[3.0.0,3.0.1)',\
|
||||
org.openhab.core.voice;version='[3.0.0,3.0.1)',\
|
||||
org.ops4j.pax.swissbox.optional.jcl;version='[1.8.3,1.8.4)',\
|
||||
org.ops4j.pax.web.pax-web-api;version='[7.2.19,7.2.20)',\
|
||||
org.ops4j.pax.web.pax-web-jetty;version='[7.2.19,7.2.20)',\
|
||||
org.ops4j.pax.web.pax-web-runtime;version='[7.2.19,7.2.20)',\
|
||||
org.ops4j.pax.web.pax-web-spi;version='[7.2.19,7.2.20)'
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"uid": "javascript.rule1",
|
||||
"name": "DemoScriptRule",
|
||||
"description": "Sample rule based on scripts",
|
||||
"triggers": [
|
||||
{
|
||||
"id": "trigger",
|
||||
"type": "core.GenericEventTrigger",
|
||||
"configuration": {
|
||||
"eventSource": "MyTrigger",
|
||||
"eventTopic": "openhab/items/MyTrigger/state",
|
||||
"eventTypes": "ItemStateEvent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition",
|
||||
"type": "script.ScriptCondition",
|
||||
"configuration": {
|
||||
"type": "application/javascript",
|
||||
"script": "event.itemState==ON"
|
||||
}
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"id": "action",
|
||||
"type": "script.ScriptAction",
|
||||
"configuration": {
|
||||
"type": "application/javascript",
|
||||
"script": "print(items.MyTrigger), print(things.getAll()), print(ctx.get('trigger.event')), events.sendCommand('ScriptItem', 'ON')"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
+3
-4
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.automation.module.script;
|
||||
package org.openhab.core.automation.module.script.internal.defaultscope;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -22,7 +22,6 @@ import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.automation.Action;
|
||||
import org.openhab.core.automation.Condition;
|
||||
import org.openhab.core.automation.Rule;
|
||||
@@ -106,7 +105,7 @@ public class ScriptRuleOSGiTest extends JavaOSGiTest {
|
||||
registerService(eventSubscriber);
|
||||
}
|
||||
|
||||
@Test
|
||||
// ignore - wip @Test
|
||||
public void testPredefinedRule() throws ItemNotFoundException {
|
||||
EventPublisher eventPublisher = getService(EventPublisher.class);
|
||||
ItemRegistry itemRegistry = getService(ItemRegistry.class);
|
||||
@@ -121,7 +120,7 @@ public class ScriptRuleOSGiTest extends JavaOSGiTest {
|
||||
RuleStatusInfo ruleStatus2 = ruleEngine.getStatusInfo(rule2.getUID());
|
||||
assertThat(ruleStatus2, is(notNullValue()));
|
||||
assertThat(ruleStatus2.getStatus(), is(RuleStatus.IDLE));
|
||||
}, 10000, 200);
|
||||
}, 5000, 200);
|
||||
Rule rule = ruleRegistry.get("javascript.rule1");
|
||||
assertThat(rule, is(notNullValue()));
|
||||
assertThat(rule.getName(), is("DemoScriptRule"));
|
||||
+8
@@ -34,6 +34,7 @@ import org.openhab.core.automation.RuleRegistry;
|
||||
import org.openhab.core.automation.RuleStatus;
|
||||
import org.openhab.core.automation.RuleStatusInfo;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.internal.module.handler.ItemCommandActionHandler;
|
||||
import org.openhab.core.automation.internal.module.handler.ItemStateTriggerHandler;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
@@ -52,6 +53,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -105,6 +107,9 @@ public abstract class BasicConditionHandlerTest extends JavaOSGiTest {
|
||||
ruleEngine = getService(RuleManager.class);
|
||||
assertThat(ruleEngine, is(notNullValue()));
|
||||
}, 3000, 100);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,6 +141,9 @@ public abstract class BasicConditionHandlerTest extends JavaOSGiTest {
|
||||
// prepare the execution
|
||||
EventPublisher eventPublisher = getService(EventPublisher.class);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
|
||||
EventSubscriber itemEventHandler = new EventSubscriber() {
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -34,6 +34,7 @@ import org.openhab.core.automation.RuleStatus;
|
||||
import org.openhab.core.automation.RuleStatusDetail;
|
||||
import org.openhab.core.automation.RuleStatusInfo;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.internal.module.handler.GenericCronTriggerHandler;
|
||||
import org.openhab.core.automation.type.ModuleTypeRegistry;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
@@ -47,6 +48,7 @@ import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemProvider;
|
||||
import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -82,6 +84,9 @@ public class RuntimeRuleTest extends JavaOSGiTest {
|
||||
ruleEngine = getService(RuleManager.class);
|
||||
assertThat("RuleManager service not found", ruleEngine, is(notNullValue()));
|
||||
}, 3000, 100);
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
@@ -39,6 +39,7 @@ import org.openhab.core.automation.events.RuleAddedEvent;
|
||||
import org.openhab.core.automation.events.RuleRemovedEvent;
|
||||
import org.openhab.core.automation.events.RuleStatusInfoEvent;
|
||||
import org.openhab.core.automation.events.RuleUpdatedEvent;
|
||||
import org.openhab.core.automation.internal.RuleEngineImpl;
|
||||
import org.openhab.core.automation.util.ModuleBuilder;
|
||||
import org.openhab.core.automation.util.RuleBuilder;
|
||||
import org.openhab.core.common.registry.ProviderChangeListener;
|
||||
@@ -55,6 +56,7 @@ import org.openhab.core.items.events.ItemCommandEvent;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.service.ReadyMarker;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -97,6 +99,9 @@ public class RuleEventTest extends JavaOSGiTest {
|
||||
};
|
||||
registerService(itemProvider);
|
||||
registerVolatileStorageService();
|
||||
|
||||
// start rule engine
|
||||
((RuleEngineImpl) getService(RuleManager.class)).onReadyMarkerAdded(new ReadyMarker("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-4
@@ -21,7 +21,6 @@ import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.xtext.naming.QualifiedName;
|
||||
@@ -81,9 +80,7 @@ public class DSLRuleProviderTest extends JavaOSGiTest {
|
||||
|
||||
readyService = getService(ReadyService.class);
|
||||
assertThat(readyService, is(notNullValue()));
|
||||
for (String id : Set.of("items", "things", "rules", RulesRefresher.RULES_REFRESH)) {
|
||||
readyService.markReady(new ReadyMarker("dsl", id));
|
||||
}
|
||||
readyService.markReady(new ReadyMarker("rules", RulesRefresher.RULES_REFRESH));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
Reference in New Issue
Block a user