diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleChannelExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleChannelExecutionContext.java new file mode 100644 index 00000000..ae6567d1 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleChannelExecutionContext.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2022 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.automation.jrule.internal.engine; + +import java.lang.reflect.Method; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRulePrecondition; + +/** + * The {@link JRuleChannelExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleChannelExecutionContext extends JRuleExecutionContext { + private final String channel; + private final String event; + + public JRuleChannelExecutionContext(JRule jRule, String logName, String[] loggingTags, String ruleName, + Method method, boolean eventParameterPresent, JRulePrecondition[] preconditions, String channel, + String event) { + super(jRule, logName, loggingTags, ruleName, method, eventParameterPresent, preconditions); + this.channel = channel; + this.event = event; + } + + public String getChannel() { + return channel; + } + + public String getEvent() { + return this.event; + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java index fccb4d1b..03be38b5 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java @@ -103,9 +103,9 @@ public class JRuleEngine implements PropertyChangeListener { private final Map> itemToRules = new HashMap<>(); - private final Map> itemToExecutionContexts = new HashMap<>(); + private final Map> itemToExecutionContexts = new HashMap<>(); - private final Map> channelToExecutionContexts = new HashMap<>(); + private final Map> channelToExecutionContexts = new HashMap<>(); private final Set itemNames = new HashSet<>(); private final Logger logger = LoggerFactory.getLogger(JRuleEngine.class); @@ -154,8 +154,8 @@ private synchronized void clearTimers() { timers.clear(); } - private void logInfo(String message, Object... paramteres) { - JRuleLog.info(logger, LOG_NAME_ENGINE, message, paramteres); + private void logInfo(String message, Object... parameters) { + JRuleLog.info(logger, LOG_NAME_ENGINE, message, parameters); } private void logDebug(String message, Object... parameters) { @@ -227,12 +227,10 @@ public void add(JRule jRule) { JRuleLog.debug(logger, logName, "Got item class: {}", itemClass); JRuleLog.info(logger, logName, "Validating JRule item: {} trigger: {} ", jRuleWhen.item(), jRuleWhen.trigger()); - addExecutionContext(jRule, logName, loggingTags, itemClass, jRuleName.value(), jRuleWhen.trigger(), - jRuleWhen.from(), jRuleWhen.to(), jRuleWhen.update(), jRuleWhen.item(), method, - jRuleEventPresent, getDoubleFromAnnotation(jRuleWhen.lt()), - getDoubleFromAnnotation(jRuleWhen.lte()), getDoubleFromAnnotation(jRuleWhen.gt()), - getDoubleFromAnnotation(jRuleWhen.gte()), getStringFromAnnotation(jRuleWhen.eq()), - getStringFromAnnotation(jRuleWhen.neq()), preconditions); + addItemExecutionContext(jRule, logName, loggingTags, jRuleName.value(), jRuleWhen.trigger(), + jRuleWhen.update(), jRuleWhen.item(), method, jRuleEventPresent, + getStringFromAnnotation(jRuleWhen.eq()), getStringFromAnnotation(jRuleWhen.neq()), + preconditions); itemNames.add(jRuleWhen.item()); ruleLoadingStatistics.addItemStateTrigger(); @@ -249,9 +247,8 @@ jRuleEventPresent, getDoubleFromAnnotation(jRuleWhen.lt()), // JRuleWhen for a channel JRuleLog.info(logger, logName, "Validating JRule channel: {} trigger: {} ", jRuleWhen.channel(), jRuleWhen.trigger()); - addChannelExecutionContext(jRule, logName, loggingTags, jRuleWhen.channel(), jRuleName.value(), - method, jRuleEventPresent, getStringFromAnnotation(jRuleWhen.eq()), - getStringFromAnnotation(jRuleWhen.neq()), preconditions); + addChannelExecutionContext(jRule, logName, loggingTags, jRuleWhen.channel(), jRuleWhen.event(), + jRuleName.value(), method, jRuleEventPresent, preconditions); ruleLoadingStatistics.addChannelTrigger(); } } @@ -323,8 +320,8 @@ private synchronized void addTimedExecution(JRule jRule, String logName, String[ timers.add(future); JRuleLog.info(logger, logName, "Scheduling timer for rule: {} hours: {} minutes: {} seconds: {} cron: {}", jRuleWhen.hours(), jRuleWhen.minutes(), jRuleWhen.seconds(), jRuleWhen.cron()); - JRuleExecutionContext executionContext = new JRuleExecutionContext(jRule, logName, loggingTags, method, - jRuleName, jRuleEventPresent, preconditions); + JRuleTimedExecutionContext executionContext = new JRuleTimedExecutionContext(jRule, logName, loggingTags, + method, jRuleName, jRuleEventPresent, preconditions); Consumer consumer = t -> { try { invokeRule(executionContext, jRuleEventPresent ? new JRuleEvent("") : null); @@ -342,27 +339,25 @@ private synchronized void addTimedExecution(JRule jRule, String logName, String[ } } - private void addExecutionContext(JRule jRule, String logName, String[] loggingTags, String itemClass, - String ruleName, String trigger, String from, String to, String update, String itemName, Method method, - boolean eventParameterPresent, Double lt, Double lte, Double gt, Double gte, String eq, String neq, - JRulePrecondition[] preconditions) { - List contextList = itemToExecutionContexts.computeIfAbsent(itemName, + private void addItemExecutionContext(JRule jRule, String logName, String[] loggingTags, String ruleName, + String trigger, String update, String itemName, Method method, boolean eventParameterPresent, String eq, + String neq, JRulePrecondition[] preconditions) { + List contextList = itemToExecutionContexts.computeIfAbsent(itemName, k -> new ArrayList<>()); - final JRuleExecutionContext context = new JRuleExecutionContext(jRule, logName, loggingTags, trigger, from, to, - update, ruleName, itemClass, itemName, method, eventParameterPresent, lt, lte, gt, gte, eq, neq, - preconditions); + final JRuleItemExecutionContext context = new JRuleItemExecutionContext(jRule, logName, loggingTags, trigger, + null, null, update, ruleName, null, null, method, eventParameterPresent, null, null, null, null, eq, + neq, preconditions); JRuleLog.debug(logger, logName, "ItemContextList add context: {}", context); contextList.add(context); } private void addChannelExecutionContext(JRule jRule, String logName, String[] loggingTags, String channel, - String ruleName, Method method, boolean eventParameterPresent, String eq, String neq, + String event, String ruleName, Method method, boolean eventParameterPresent, JRulePrecondition[] preconditions) { - List contextList = channelToExecutionContexts.computeIfAbsent(channel, + List contextList = channelToExecutionContexts.computeIfAbsent(channel, k -> new ArrayList<>()); - final JRuleExecutionContext context = new JRuleExecutionContext(jRule, logName, loggingTags, null, null, null, - null, ruleName, null, null, method, eventParameterPresent, null, null, null, null, eq, neq, - preconditions); + final JRuleChannelExecutionContext context = new JRuleChannelExecutionContext(jRule, logName, loggingTags, + ruleName, method, eventParameterPresent, preconditions, channel, event); JRuleLog.debug(logger, logName, "ChannelContextList add context: {}", context); contextList.add(context); } @@ -380,19 +375,23 @@ public void propertyChange(PropertyChangeEvent evt) { } private void handleChannelEvent(ChannelTriggeredEvent channelEvent) { - List executionContexts = channelToExecutionContexts + List executionContexts = channelToExecutionContexts .get(channelEvent.getChannel().toString()); if (executionContexts == null || executionContexts.isEmpty()) { logDebug("No execution context for channelEvent: {}", channelEvent); return; } - executionContexts.forEach(context -> invokeWhenMatchParameters(context, - new JRuleEvent(channelEvent.getEvent(), channelEvent.getChannel().toString()))); + executionContexts.stream().filter(context -> context.getChannel().equals(channelEvent.getChannel().toString())) + .filter(context -> context.getEvent().equals(channelEvent.getEvent())).forEach(context -> { + JRuleLog.debug(logger, context.getLogName(), "invoke when context matches"); + invokeRule(context, new JRuleEvent(channelEvent.getEvent(), channelEvent.getChannel().toString(), + channelEvent.getEvent())); + }); } private void handleEventUpdate(Event event) { final String itemName = getItemNameFromEvent(event); - final List executionContexts = itemToExecutionContexts.get(itemName); + final List executionContexts = itemToExecutionContexts.get(itemName); if (executionContexts == null || executionContexts.isEmpty()) { logDebug("No execution context for changeEvent "); return; @@ -474,7 +473,7 @@ private Boolean evaluateComparatorParameters(Double gt, Double gte, Double lt, D return null; } - private void invokeWhenMatchParameters(JRuleExecutionContext context, @NonNull JRuleEvent jRuleEvent) { + private void invokeWhenMatchParameters(JRuleItemExecutionContext context, @NonNull JRuleEvent jRuleEvent) { JRuleLog.debug(logger, context.getLogName(), "invoke when context matches"); if (context.isComparatorOperation()) { @@ -516,7 +515,7 @@ private String getItemNameFromEvent(Event event) { } private void invokeRule(JRuleExecutionContext context, JRuleEvent event) { - Object invokationResult = config.isExecutorsEnabled() ? invokeRuleInSeparateThread(context, event) + Object invocationResult = config.isExecutorsEnabled() ? invokeRuleInSeparateThread(context, event) : invokeRuleSingleThread(context, event); } @@ -549,12 +548,12 @@ private Object invokeRuleInternal(JRuleExecutionContext context, JRuleEvent even MDC.put(MDC_KEY_RULE, context.getRuleName()); Arrays.stream(context.getLoggingTags()).forEach(s -> MDC.put(s, s)); return context.isEventParameterPresent() ? method.invoke(rule, event) : method.invoke(rule); - } catch (IllegalAccessException | IllegalArgumentException | SecurityException e) { - JRuleLog.error(logger, context.getRuleName(), "Error {}", e); } catch (InvocationTargetException e) { Throwable ex = e.getCause() != null ? e.getCause() : null; JRuleLog.error(logger, context.getRuleName(), "Error message: {}", ex.getMessage()); JRuleLog.error(logger, context.getRuleName(), "Error Stacktrace: {}", getStackTraceAsString(ex)); + } catch (Exception e) { + JRuleLog.error(logger, context.getRuleName(), "Error {}", e); } finally { Arrays.stream(context.getLoggingTags()).forEach(MDC::remove); MDC.remove(MDC_KEY_RULE); diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleExecutionContext.java index 30c52b8c..820108e3 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleExecutionContext.java @@ -22,27 +22,12 @@ * * @author Joseph (Seaside) Hagberg - Initial contribution */ -public class JRuleExecutionContext { - private static final String FROM_PREFIX = " from "; - private static final String TO_PREFIX = " to "; - private static final String SPACE = " "; +public abstract class JRuleExecutionContext { private final String logName; - private final String trigger; - private final String ruleName; - private final String itemClass; - private final String itemName; - private final String from; - private final String to; - private final Double gt; - private final Double gte; - private final Double lt; - private final Double lte; - private final String eq; - private final String neq; - private final String update; - private final JRule jRule; - private final Method method; - private final boolean eventParameterPresent; + protected final String ruleName; + protected final JRule jRule; + protected final Method method; + protected final boolean eventParameterPresent; private final String[] loggingTags; public JRulePrecondition[] getPreconditions() { @@ -51,111 +36,17 @@ public JRulePrecondition[] getPreconditions() { private final JRulePrecondition[] preconditions; - public JRuleExecutionContext(JRule jRule, String logName, String[] loggingTags, String trigger, String from, - String to, String update, String ruleName, String itemClass, String itemName, Method method, - boolean eventParameterPresent, Double lt, Double lte, Double gt, Double gte, String eq, String neq, - JRulePrecondition[] preconditions) { + public JRuleExecutionContext(JRule jRule, String logName, String[] loggingTags, String ruleName, Method method, + boolean eventParameterPresent, JRulePrecondition[] preconditions) { this.logName = logName; this.loggingTags = loggingTags; - this.gt = gt; - this.gte = gte; - this.lt = lt; - this.lte = lte; - this.eq = eq; - this.neq = neq; this.jRule = jRule; - this.trigger = trigger; - this.from = from; - this.to = to; - this.update = update; this.ruleName = ruleName; - this.itemClass = itemClass; - this.itemName = itemName; this.method = method; this.eventParameterPresent = eventParameterPresent; this.preconditions = preconditions; } - public JRuleExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, String ruleName, - boolean jRuleEventPresent, JRulePrecondition[] preconditions) { - this(jRule, logName, loggingTags, null, null, null, null, ruleName, null, null, method, jRuleEventPresent, null, - null, null, null, null, null, preconditions); - } - - @Override - public String toString() { - return "JRuleExecutionContext [trigger=" + trigger + ", ruleName=" + ruleName + ", itemClass=" + itemClass - + ", itemName=" + itemName + ", from=" + from + ", to=" + to + ", gt=" + gt + ", gte=" + gte + ", lt=" - + lt + ", lte=" + lte + ", eq=" + eq + ", update=" + update + ", jRule=" + jRule + ", method=" + method - + ", eventParameterPresent=" + eventParameterPresent + "]"; - } - - public String getTrigger() { - return trigger; - } - - public String getTriggerFullString() { - if (from != null && !from.isEmpty() && to != null && !to.isEmpty()) { - return buildFromToString(trigger, from, to); - } - if (from != null && !from.isEmpty()) { - return buildFromToString(trigger, from, null); - } - if (to != null && !to.isEmpty()) { - return buildFromToString(trigger, null, to); - } - if (update != null && !update.isEmpty()) { - return buildUpdateString(trigger, update); - } - return trigger; - } - - private String buildUpdateString(String trigger, String update) { - final StringBuilder builder = new StringBuilder(); - builder.append(trigger); - builder.append(SPACE); - builder.append(update); - return builder.toString(); - } - - private String buildFromToString(String trigger, String from, String to) { - final StringBuilder builder = new StringBuilder(); - builder.append(trigger); - if (from != null) { - builder.append(FROM_PREFIX); - builder.append(from); - } - if (to != null) { - builder.append(TO_PREFIX); - builder.append(to); - } - return builder.toString(); - } - - public String getItemClass() { - return itemClass; - } - - public String getItemName() { - return itemName; - } - - public String getFrom() { - return from; - } - - public String getTo() { - return to; - } - - public String getUpdate() { - return update; - } - - public JRule getjRule() { - return jRule; - } - public String getRuleName() { return ruleName; } @@ -172,38 +63,6 @@ public boolean isEventParameterPresent() { return eventParameterPresent; } - public Double getGt() { - return gt; - } - - public Double getGte() { - return gte; - } - - public Double getLt() { - return lt; - } - - public Double getLte() { - return lte; - } - - public String getEq() { - return eq; - } - - public String getNeq() { - return neq; - } - - public boolean isNumericOperation() { - return lte != null || lt != null || gt != null || gte != null; - } - - public boolean isComparatorOperation() { - return lte != null || lt != null || gt != null || gte != null || eq != null || neq != null; - } - public String getLogName() { return logName; } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleItemExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleItemExecutionContext.java new file mode 100644 index 00000000..9e08b34a --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleItemExecutionContext.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2010-2022 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.automation.jrule.internal.engine; + +import java.lang.reflect.Method; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRulePrecondition; + +/** + * The {@link JRuleItemExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleItemExecutionContext extends JRuleExecutionContext { + private static final String FROM_PREFIX = " from "; + private static final String TO_PREFIX = " to "; + private static final String SPACE = " "; + + private final String itemClass; + private final String itemName; + private final String trigger; + private final String update; + private final String from; + private final String to; + private final Double gt; + private final Double gte; + private final Double lt; + private final Double lte; + protected final String eq; + protected final String neq; + + public JRuleItemExecutionContext(JRule jRule, String logName, String[] loggingTags, String trigger, String from, + String to, String update, String ruleName, String itemClass, String itemName, Method method, + boolean eventParameterPresent, Double lt, Double lte, Double gt, Double gte, String eq, String neq, + JRulePrecondition[] preconditions) { + super(jRule, logName, loggingTags, ruleName, method, eventParameterPresent, preconditions); + this.itemClass = itemClass; + this.itemName = itemName; + this.trigger = trigger; + this.update = update; + this.from = from; + this.to = to; + this.gt = gt; + this.gte = gte; + this.lt = lt; + this.lte = lte; + this.eq = eq; + this.neq = neq; + } + + public String getItemName() { + return itemName; + } + + public Double getGt() { + return gt; + } + + public Double getGte() { + return gte; + } + + public Double getLt() { + return lt; + } + + public Double getLte() { + return lte; + } + + public String getEq() { + return eq; + } + + public String getNeq() { + return neq; + } + + public String getTrigger() { + return trigger; + } + + public boolean isComparatorOperation() { + return lte != null || lt != null || gt != null || gte != null || eq != null || neq != null; + } + + private String buildFromToString(String trigger, String from, String to) { + final StringBuilder builder = new StringBuilder(); + builder.append(trigger); + if (from != null) { + builder.append(FROM_PREFIX); + builder.append(from); + } + if (to != null) { + builder.append(TO_PREFIX); + builder.append(to); + } + return builder.toString(); + } + + private String buildUpdateString(String trigger, String update) { + return trigger + SPACE + update; + } + + public String getTriggerFullString() { + if (from != null && !from.isEmpty() && to != null && !to.isEmpty()) { + return buildFromToString(trigger, from, to); + } + if (from != null && !from.isEmpty()) { + return buildFromToString(trigger, from, null); + } + if (to != null && !to.isEmpty()) { + return buildFromToString(trigger, null, to); + } + if (update != null && !update.isEmpty()) { + return buildUpdateString(trigger, update); + } + return trigger; + } + + @Override + public String toString() { + return "JRuleExecutionContext [trigger=" + trigger + ", ruleName=" + ruleName + ", itemClass=" + itemClass + + ", itemName=" + itemName + ", from=" + from + ", to=" + to + ", gt=" + gt + ", gte=" + gte + ", lt=" + + lt + ", lte=" + lte + ", eq=" + eq + ", update=" + update + ", jRule=" + jRule + ", method=" + method + + ", eventParameterPresent=" + eventParameterPresent + "]"; + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleTimedExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleTimedExecutionContext.java new file mode 100644 index 00000000..2ab318aa --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleTimedExecutionContext.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.automation.jrule.internal.engine; + +import java.lang.reflect.Method; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRulePrecondition; + +/** + * The {@link JRuleTimedExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleTimedExecutionContext extends JRuleExecutionContext { + public JRuleTimedExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, String ruleName, + boolean jRuleEventPresent, JRulePrecondition[] preconditions) { + super(jRule, logName, loggingTags, ruleName, method, jRuleEventPresent, preconditions); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java index 209bfd3f..53abf54a 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java @@ -25,6 +25,7 @@ public class JRuleEvent { private final JRuleEventState state; private final JRuleEventState oldState; + private String event; private String memberName; @@ -43,10 +44,11 @@ public JRuleEvent(String value, String oldValue, String itemName, String memberN this.memberName = memberName; } - public JRuleEvent(String value, String channel) { + public JRuleEvent(String value, String channel, String event) { this.state = new JRuleEventState(value); this.oldState = null; this.channel = channel; + this.event = event; } public JRuleEventState getState() { @@ -101,7 +103,7 @@ public String getChannel() { @Override public String toString() { - return String.format("JRuleEvent [state=%s, oldState=%s, memberName=%s, itemName=%s, channel=%s]", state, - oldState, memberName, itemName, channel); + return String.format("JRuleEvent [state=%s, oldState=%s, memberName=%s, itemName=%s, channel=%s, event=%s]", + state, oldState, memberName, itemName, channel); } } diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java index 2c3c4752..f2229f33 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java @@ -39,6 +39,8 @@ String channel() default ""; + String event() default ""; + String trigger() default ""; String update() default "";