110
110
* @author Benedikt Niehues - change behavior for unregistering ModuleHandler
111
111
* @author Markus Rathgeb - use a managed rule
112
112
* @author Ana Dimova - new reference syntax: list[index], map["key"], bean.field
113
+ * @author Florian Hotze - add support for script condition/action compilation
113
114
*/
114
115
@ Component (immediate = true , service = { RuleManager .class })
115
116
@ NonNullByDefault
@@ -819,6 +820,8 @@ public synchronized void setEnabled(String uid, boolean enable) {
819
820
* <ul>
820
821
* <li>Set the module handlers. If there are errors, set the rule status (handler error) and return with error
821
822
* indication.
823
+ * <li>Compile the conditions and actions. If there are errors, set the rule status (handler error) and return with
824
+ * indication.
822
825
* <li>Register the rule. Set the rule status and return with success indication.
823
826
* </ul>
824
827
*
@@ -845,6 +848,11 @@ private boolean activateRule(final WrappedRule rule) {
845
848
return false ;
846
849
}
847
850
851
+ // Compile the conditions and actions and so check if they are valid.
852
+ if (!compileRule (rule )) {
853
+ return false ;
854
+ }
855
+
848
856
// Register the rule and set idle status.
849
857
register (rule );
850
858
setStatus (ruleUID , new RuleStatusInfo (RuleStatus .IDLE ));
@@ -862,6 +870,58 @@ private boolean activateRule(final WrappedRule rule) {
862
870
return true ;
863
871
}
864
872
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
+
865
925
@ Override
866
926
public @ Nullable RuleStatusInfo getStatusInfo (String ruleUID ) {
867
927
final WrappedRule rule = managedRules .get (ruleUID );
@@ -1134,6 +1194,32 @@ private Map<String, Object> getContext(String ruleUID, @Nullable Set<Connection>
1134
1194
return context ;
1135
1195
}
1136
1196
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
+
1137
1223
/**
1138
1224
* This method checks if all rule's condition are satisfied or not.
1139
1225
*
@@ -1163,6 +1249,31 @@ private boolean calculateConditions(WrappedRule rule) {
1163
1249
return true ;
1164
1250
}
1165
1251
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
+
1166
1277
/**
1167
1278
* This method evaluates actions of the {@link Rule} and set their {@link Output}s when they exist.
1168
1279
*
@@ -1435,14 +1546,28 @@ public String getOutputName() {
1435
1546
1436
1547
@ Override
1437
1548
public void onReadyMarkerAdded (ReadyMarker readyMarker ) {
1438
- executeRulesWithStartLevel ();
1549
+ compileRules ();
1439
1550
}
1440
1551
1441
1552
@ Override
1442
1553
public void onReadyMarkerRemoved (ReadyMarker readyMarker ) {
1443
1554
started = false ;
1444
1555
}
1445
1556
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
+
1446
1571
private void executeRulesWithStartLevel () {
1447
1572
getScheduledExecutor ().submit (() -> {
1448
1573
ruleRegistry .getAll ().stream () //
0 commit comments