Skip to content

Commit 4688e50

Browse files
committed
RuleEngineImpl: Implement compilation of rules on start-up and when activating a rule
Signed-off-by: Florian Hotze <[email protected]>
1 parent d1b1d0b commit 4688e50

File tree

1 file changed

+126
-1
lines changed
  • bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal

1 file changed

+126
-1
lines changed

bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/RuleEngineImpl.java

+126-1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
* @author Benedikt Niehues - change behavior for unregistering ModuleHandler
111111
* @author Markus Rathgeb - use a managed rule
112112
* @author Ana Dimova - new reference syntax: list[index], map["key"], bean.field
113+
* @author Florian Hotze - add support for script condition/action compilation
113114
*/
114115
@Component(immediate = true, service = { RuleManager.class })
115116
@NonNullByDefault
@@ -819,6 +820,8 @@ public synchronized void setEnabled(String uid, boolean enable) {
819820
* <ul>
820821
* <li>Set the module handlers. If there are errors, set the rule status (handler error) and return with error
821822
* indication.
823+
* <li>Compile the conditions and actions. If there are errors, set the rule status (handler error) and return with
824+
* indication.
822825
* <li>Register the rule. Set the rule status and return with success indication.
823826
* </ul>
824827
*
@@ -845,6 +848,11 @@ private boolean activateRule(final WrappedRule rule) {
845848
return false;
846849
}
847850

851+
// Compile the conditions and actions and so check if they are valid.
852+
if (!compileRule(rule)) {
853+
return false;
854+
}
855+
848856
// Register the rule and set idle status.
849857
register(rule);
850858
setStatus(ruleUID, new RuleStatusInfo(RuleStatus.IDLE));
@@ -862,6 +870,58 @@ private boolean activateRule(final WrappedRule rule) {
862870
return true;
863871
}
864872

873+
/**
874+
* Compile the conditions and actions of the given rule.
875+
* If there are errors, set the rule status (handler error) and return with indication.
876+
*
877+
* @param rule the rule whose conditions and actions should be compiled
878+
* @return true if compilation succeeded, otherwise false
879+
*/
880+
private boolean compileRule(final WrappedRule rule) {
881+
try {
882+
compileConditions(rule);
883+
compileActions(rule);
884+
return true;
885+
} catch (Throwable t) {
886+
setStatus(rule.getUID(), new RuleStatusInfo(RuleStatus.UNINITIALIZED,
887+
RuleStatusDetail.HANDLER_INITIALIZING_ERROR, t.getMessage()));
888+
unregister(rule);
889+
return false;
890+
}
891+
}
892+
893+
/**
894+
* Compile the conditions and actions of the given rule.
895+
* If there are errors, set the rule status (handler error).
896+
*
897+
* @param ruleUID the UID of the rule whose conditions and actions should be compiled
898+
*/
899+
private void compileRule(String ruleUID) {
900+
final WrappedRule rule = getManagedRule(ruleUID);
901+
if (rule == null) {
902+
logger.warn("Failed to compile rule '{}': Invalid Rule UID", ruleUID);
903+
return;
904+
}
905+
synchronized (this) {
906+
final RuleStatus ruleStatus = getRuleStatus(ruleUID);
907+
if (ruleStatus != null && ruleStatus != RuleStatus.IDLE) {
908+
logger.error("Failed to compile rule ‘{}' with status '{}'", ruleUID, ruleStatus.name());
909+
return;
910+
}
911+
// change state to INITIALIZING
912+
setStatus(ruleUID, new RuleStatusInfo(RuleStatus.INITIALIZING));
913+
}
914+
if (!compileRule(rule)) {
915+
return;
916+
}
917+
// change state to IDLE only if the rule has not been DISABLED.
918+
synchronized (this) {
919+
if (getRuleStatus(ruleUID) == RuleStatus.INITIALIZING) {
920+
setStatus(ruleUID, new RuleStatusInfo(RuleStatus.IDLE));
921+
}
922+
}
923+
}
924+
865925
@Override
866926
public @Nullable RuleStatusInfo getStatusInfo(String ruleUID) {
867927
final WrappedRule rule = managedRules.get(ruleUID);
@@ -1134,6 +1194,32 @@ private Map<String, Object> getContext(String ruleUID, @Nullable Set<Connection>
11341194
return context;
11351195
}
11361196

1197+
/**
1198+
* This method compiles conditions of the {@link Rule} when they exist.
1199+
* It is called when the rule is initialized.
1200+
*
1201+
* @param rule compiled rule.
1202+
*/
1203+
private void compileConditions(WrappedRule rule) {
1204+
final Collection<WrappedCondition> conditions = rule.getConditions();
1205+
if (conditions.isEmpty()) {
1206+
return;
1207+
}
1208+
for (WrappedCondition wrappedCondition : conditions) {
1209+
final Condition condition = wrappedCondition.unwrap();
1210+
ConditionHandler cHandler = wrappedCondition.getModuleHandler();
1211+
if (cHandler != null) {
1212+
try {
1213+
cHandler.compile();
1214+
} catch (Throwable t) {
1215+
String errMessage = "Failed to pre-compile condition: " + condition.getId() + "(" + t.getMessage()
1216+
+ ")";
1217+
throw new RuntimeException(errMessage, t);
1218+
}
1219+
}
1220+
}
1221+
}
1222+
11371223
/**
11381224
* This method checks if all rule's condition are satisfied or not.
11391225
*
@@ -1163,6 +1249,31 @@ private boolean calculateConditions(WrappedRule rule) {
11631249
return true;
11641250
}
11651251

1252+
/**
1253+
* This method compiles actions of the {@link Rule} when they exist.
1254+
* It is called when the rule is initialized.
1255+
*
1256+
* @param rule compiled rule.
1257+
*/
1258+
private void compileActions(WrappedRule rule) {
1259+
final Collection<WrappedAction> actions = rule.getActions();
1260+
if (actions.isEmpty()) {
1261+
return;
1262+
}
1263+
for (WrappedAction wrappedAction : actions) {
1264+
final Action action = wrappedAction.unwrap();
1265+
ActionHandler aHandler = wrappedAction.getModuleHandler();
1266+
if (aHandler != null) {
1267+
try {
1268+
aHandler.compile();
1269+
} catch (Throwable t) {
1270+
String errMessage = "Failed to pre-compile action: " + action.getId() + "(" + t.getMessage() + ")";
1271+
throw new RuntimeException(errMessage, t);
1272+
}
1273+
}
1274+
}
1275+
}
1276+
11661277
/**
11671278
* This method evaluates actions of the {@link Rule} and set their {@link Output}s when they exist.
11681279
*
@@ -1435,14 +1546,28 @@ public String getOutputName() {
14351546

14361547
@Override
14371548
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
1438-
executeRulesWithStartLevel();
1549+
compileRules();
14391550
}
14401551

14411552
@Override
14421553
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
14431554
started = false;
14441555
}
14451556

1557+
/**
1558+
* This method compiles the conditions and actions of all rules. It is called when the rule engine is started.
1559+
* By compiling when the rule engine is started, we make sure all conditions and actions are compiled, even if their
1560+
* handlers weren't available when the rule was added to the rule engine.
1561+
*/
1562+
private void compileRules() {
1563+
getScheduledExecutor().submit(() -> {
1564+
ruleRegistry.getAll().forEach(r -> {
1565+
compileRule(r.getUID());
1566+
});
1567+
executeRulesWithStartLevel();
1568+
});
1569+
}
1570+
14461571
private void executeRulesWithStartLevel() {
14471572
getScheduledExecutor().submit(() -> {
14481573
ruleRegistry.getAll().stream() //

0 commit comments

Comments
 (0)