1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.kew.rule;
18
19 import org.apache.commons.lang.ObjectUtils;
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.log4j.Logger;
22 import org.kuali.rice.core.exception.RiceRuntimeException;
23 import org.kuali.rice.core.util.ClassLoaderUtils;
24 import org.kuali.rice.kew.actionrequest.*;
25 import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
26 import org.kuali.rice.kew.engine.RouteContext;
27 import org.kuali.rice.kew.engine.node.NodeState;
28 import org.kuali.rice.kew.engine.node.RouteNode;
29 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
30 import org.kuali.rice.kew.exception.WorkflowException;
31 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
32 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
33 import org.kuali.rice.kew.rule.bo.RuleAttribute;
34 import org.kuali.rice.kew.rule.bo.RuleTemplate;
35 import org.kuali.rice.kew.rule.bo.RuleTemplateAttribute;
36 import org.kuali.rice.kew.rule.service.RuleDelegationService;
37 import org.kuali.rice.kew.rule.service.RuleService;
38 import org.kuali.rice.kew.service.KEWServiceLocator;
39 import org.kuali.rice.kew.user.RoleRecipient;
40 import org.kuali.rice.kew.util.KEWConstants;
41 import org.kuali.rice.kew.util.PerformanceLogger;
42 import org.kuali.rice.kew.util.ResponsibleParty;
43 import org.kuali.rice.kew.util.Utilities;
44
45 import java.lang.reflect.Method;
46 import java.sql.Timestamp;
47 import java.util.ArrayList;
48 import java.util.Date;
49 import java.util.List;
50 import java.util.Map;
51
52
53
54
55
56
57
58
59
60
61
62
63 public class FlexRM {
64
65 private static final Logger LOG = Logger.getLogger(FlexRM.class);
66
67
68
69
70
71 public static final String DEFAULT_RULE_SELECTOR = "Template";
72
73
74
75 private static final String RULE_SELECTOR_PACKAGE = "org.kuali.rice.kew.rule";
76
77
78
79 private static final String RULE_SELECTOR_SUFFIX= "RuleSelector";
80
81 private final Timestamp effectiveDate;
82
83
84
85
86 private int selectedRules;
87
88 public FlexRM() {
89 this.effectiveDate = null;
90 }
91
92 public FlexRM(Timestamp effectiveDate) {
93 this.effectiveDate = effectiveDate;
94 }
95
96
97
98
99
100
101 protected RuleSelector loadRuleSelector(RouteNode routeNodeDef, RouteNodeInstance nodeInstance) throws WorkflowException {
102
103 NodeState ns = null;
104 if (nodeInstance != null) {
105 ns = nodeInstance.getNodeState(KEWConstants.RULE_SELECTOR_NODE_STATE_KEY);
106 }
107 String ruleSelectorName = null;
108 if (ns != null) {
109 ruleSelectorName = ns.getValue();
110 } else {
111
112 Map<String, String> nodeCfgParams = Utilities.getKeyValueCollectionAsMap(
113 routeNodeDef.
114 getConfigParams());
115 ruleSelectorName = nodeCfgParams.get(RouteNode.RULE_SELECTOR_CFG_KEY);
116 }
117
118 if (ruleSelectorName == null) {
119 ruleSelectorName = DEFAULT_RULE_SELECTOR;
120 }
121 ruleSelectorName = StringUtils.capitalize(ruleSelectorName);
122
123
124 String className = RULE_SELECTOR_PACKAGE + "." + ruleSelectorName + RULE_SELECTOR_SUFFIX;
125 Class<?> ruleSelectorClass;
126 try {
127 ruleSelectorClass = ClassLoaderUtils.getDefaultClassLoader().loadClass(className);
128 } catch (ClassNotFoundException cnfe) {
129 throw new WorkflowException("Rule selector implementation '" + className + "' not found", cnfe);
130 }
131 if (!RuleSelector.class.isAssignableFrom(ruleSelectorClass)) {
132 throw new WorkflowException("Specified class '" + ruleSelectorClass + "' does not implement RuleSelector interface");
133 }
134 RuleSelector ruleSelector;
135 try {
136 ruleSelector = ((Class<RuleSelector>) ruleSelectorClass).newInstance();
137 } catch (Exception e) {
138 if (e instanceof RuntimeException) {
139 throw (RuntimeException)e;
140 }
141 throw new WorkflowException("Error instantiating rule selector implementation '" + ruleSelectorClass + "'", e);
142 }
143
144 return ruleSelector;
145 }
146
147
148
149
150
151
152
153
154
155 public List<ActionRequestValue> getActionRequests(DocumentRouteHeaderValue routeHeader, RouteNodeInstance nodeInstance, String ruleTemplateName)
156 throws WorkflowException {
157 return getActionRequests(routeHeader, nodeInstance.getRouteNode(), nodeInstance, ruleTemplateName);
158 }
159
160
161
162
163
164
165
166
167
168
169 public List<ActionRequestValue> getActionRequests(DocumentRouteHeaderValue routeHeader, RouteNode routeNodeDef, RouteNodeInstance nodeInstance, String ruleTemplateName)
170 throws WorkflowException {
171 RouteContext context = RouteContext.getCurrentRouteContext();
172
173
174 if (context.getDocument() == null) {
175 context.setDocument(routeHeader);
176 }
177 if (context.getNodeInstance() == null) {
178 context.setNodeInstance(nodeInstance);
179 }
180
181 LOG.debug("Making action requests for document " + routeHeader.getRouteHeaderId());
182
183 RuleSelector ruleSelector = loadRuleSelector(routeNodeDef, nodeInstance);
184
185 List<Rule> rules = ruleSelector.selectRules(context, routeHeader, nodeInstance, ruleTemplateName, effectiveDate);
186
187
188
189
190
191
192
193
194
195 if (ruleSelector instanceof TemplateRuleSelector) {
196 selectedRules += ((TemplateRuleSelector) ruleSelector).getNumberOfSelectedRules();
197 }
198
199 PerformanceLogger performanceLogger = new PerformanceLogger();
200
201 ActionRequestFactory arFactory = new ActionRequestFactory(routeHeader, context.getNodeInstance());
202
203 List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
204 if (rules != null) {
205 LOG.info("Total number of rules selected by RuleSelector for documentType=" + routeHeader.getDocumentType().getName() + " and ruleTemplate=" + ruleTemplateName + ": " + rules.size());
206 for (Rule rule: rules) {
207 RuleExpressionResult result = rule.evaluate(rule, context);
208 if (result.isSuccess() && result.getResponsibilities() != null) {
209
210 makeActionRequests(arFactory, result.getResponsibilities(), context, rule.getDefinition(), routeHeader, null, null);
211 }
212 }
213 }
214 actionRequests = new ArrayList<ActionRequestValue>(arFactory.getRequestGraphs());
215 performanceLogger.log("Time to make action request for template " + ruleTemplateName);
216
217 return actionRequests;
218 }
219
220 public ResponsibleParty resolveResponsibilityId(Long responsibilityId) {
221 if (responsibilityId == null) {
222 throw new IllegalArgumentException("A null responsibilityId was passed to resolve responsibility!");
223 }
224 RuleResponsibility resp = getRuleService().findRuleResponsibility(responsibilityId);
225 ResponsibleParty responsibleParty = new ResponsibleParty();
226 if (resp!=null && resp.isUsingRole()) {
227 responsibleParty.setRoleName(resp.getResolvedRoleName());
228 } else if (resp!=null && resp.isUsingWorkflowUser()) {
229 responsibleParty.setPrincipalId(resp.getRuleResponsibilityName());
230 } else if (resp!=null && resp.isUsingGroup()) {
231 responsibleParty.setGroupId(resp.getRuleResponsibilityName());
232 } else {
233 throw new RiceRuntimeException("Failed to resolve responsibility from responsibility ID " + responsibilityId + ". Responsibility was an invalid type: " + resp);
234 }
235 return responsibleParty;
236 }
237
238 private void makeActionRequests(ActionRequestFactory arFactory, RouteContext context, RuleBaseValues rule, DocumentRouteHeaderValue routeHeader, ActionRequestValue parentRequest, RuleDelegation ruleDelegation)
239 throws WorkflowException {
240
241 List<RuleResponsibility> responsibilities = rule.getResponsibilities();
242 makeActionRequests(arFactory, responsibilities, context, rule, routeHeader, parentRequest, ruleDelegation);
243 }
244
245 public void makeActionRequests(ActionRequestFactory arFactory, List<RuleResponsibility> responsibilities, RouteContext context, RuleBaseValues rule, DocumentRouteHeaderValue routeHeader, ActionRequestValue parentRequest, RuleDelegation ruleDelegation)
246 throws WorkflowException {
247
248
249 for (RuleResponsibility responsibility : responsibilities)
250 {
251
252
253 if (responsibility.isUsingRole())
254 {
255 makeRoleActionRequests(arFactory, context, rule, responsibility, routeHeader, parentRequest, ruleDelegation);
256 } else
257 {
258 makeActionRequest(arFactory, context, rule, routeHeader, responsibility, parentRequest, ruleDelegation);
259 }
260
261
262
263 }
264 }
265
266 private void buildDelegationGraph(ActionRequestFactory arFactory, RouteContext context,
267 RuleBaseValues delegationRule, DocumentRouteHeaderValue routeHeaderValue, ActionRequestValue parentRequest, RuleDelegation ruleDelegation) throws WorkflowException {
268 context.setActionRequest(parentRequest);
269 if (delegationRule.isActive(new Date())) {
270 for (RuleResponsibility delegationResp : delegationRule.getResponsibilities())
271 {
272 if (delegationResp.isUsingRole())
273 {
274 makeRoleActionRequests(arFactory, context, delegationRule, delegationResp, routeHeaderValue, parentRequest, ruleDelegation);
275 } else if (delegationRule.isMatch(context.getDocumentContent()))
276 {
277 makeActionRequest(arFactory, context, delegationRule, routeHeaderValue, delegationResp, parentRequest, ruleDelegation);
278 }
279 }
280 }
281 }
282
283
284
285
286 private void makeRoleActionRequests(ActionRequestFactory arFactory, RouteContext context,
287 RuleBaseValues rule, RuleResponsibility resp, DocumentRouteHeaderValue routeHeader, ActionRequestValue parentRequest,
288 RuleDelegation ruleDelegation) throws WorkflowException
289 {
290 String roleName = resp.getResolvedRoleName();
291 RoleAttribute roleAttribute = resp.resolveRoleAttribute();
292 setRuleAttribute(roleAttribute, rule, resp.getRoleAttributeName());
293 List<String> qualifiedRoleNames = new ArrayList<String>();
294 if (parentRequest != null && parentRequest.getQualifiedRoleName() != null) {
295 qualifiedRoleNames.add(parentRequest.getQualifiedRoleName());
296 } else {
297 qualifiedRoleNames.addAll(roleAttribute.getQualifiedRoleNames(roleName, context.getDocumentContent()));
298 }
299 for (String qualifiedRoleName : qualifiedRoleNames)
300 {
301 if (parentRequest == null && isDuplicateActionRequestDetected(routeHeader, context.getNodeInstance(), resp, qualifiedRoleName))
302 {
303 continue;
304 }
305
306 ResolvedQualifiedRole resolvedRole = roleAttribute.resolveQualifiedRole(context, roleName, qualifiedRoleName);
307 RoleRecipient recipient = new RoleRecipient(roleName, qualifiedRoleName, resolvedRole);
308 if (parentRequest == null)
309 {
310 ActionRequestValue roleRequest = arFactory.addRoleRequest(recipient, resp.getActionRequestedCd(), resp.getApprovePolicy(), resp.getPriority(), resp.getResponsibilityId(), rule
311 .getForceAction(), rule.getDescription(), rule.getRuleBaseValuesId());
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327 List<RuleDelegation> ruleDelegations = getRuleDelegationService().findByResponsibilityId(resp.getResponsibilityId());
328 if (ruleDelegations != null && !ruleDelegations.isEmpty())
329 {
330
331 for (ActionRequestValue request : roleRequest.getChildrenRequests())
332 {
333 for (RuleDelegation childRuleDelegation : ruleDelegations)
334 {
335 buildDelegationGraph(arFactory, context, childRuleDelegation.getDelegationRuleBaseValues(), routeHeader, request, childRuleDelegation);
336 }
337 }
338 }
339
340 } else
341 {
342 arFactory.addDelegationRoleRequest(parentRequest, resp.getApprovePolicy(), recipient, resp.getResponsibilityId(), rule.getForceAction(), ruleDelegation.getDelegationType(), rule.getDescription(), rule.getRuleBaseValuesId());
343 }
344 }
345 }
346
347
348
349
350 private void setRuleAttribute(RoleAttribute roleAttribute, RuleBaseValues rule, String roleAttributeName) {
351
352 Method setRuleAttributeMethod = null;
353 try {
354 setRuleAttributeMethod = roleAttribute.getClass().getMethod("setRuleAttribute", RuleAttribute.class);
355 } catch (NoSuchMethodException e) {}
356 if (setRuleAttributeMethod == null) {
357 return;
358 }
359
360 RuleTemplate ruleTemplate = rule.getRuleTemplate();
361 if (ruleTemplate != null) {
362 for (RuleTemplateAttribute ruleTemplateAttribute : ruleTemplate.getActiveRuleTemplateAttributes())
363 {
364 RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
365 if (ruleAttribute.getClassName().equals(roleAttributeName))
366 {
367
368 try
369 {
370 setRuleAttributeMethod.invoke(roleAttribute, ruleAttribute);
371 break;
372 } catch (Exception e)
373 {
374 throw new WorkflowRuntimeException("Failed to set RuleAttribute on our RoleAttribute!", e);
375 }
376 }
377 }
378 }
379 }
380
381
382
383
384
385 private void makeActionRequest(ActionRequestFactory arFactory, RouteContext context, RuleBaseValues rule, DocumentRouteHeaderValue routeHeader, RuleResponsibility resp, ActionRequestValue parentRequest,
386 RuleDelegation ruleDelegation) throws WorkflowException {
387 if (parentRequest == null && isDuplicateActionRequestDetected(routeHeader, context.getNodeInstance(), resp, null)) {
388 return;
389 }
390 Recipient recipient;
391 if (resp.isUsingWorkflowUser()) {
392 recipient = new KimPrincipalRecipient(resp.getPrincipal());
393 } else if (resp.isUsingGroup()) {
394 recipient = new KimGroupRecipient(resp.getGroup());
395 } else {
396 throw new RiceRuntimeException("Illegal rule responsibility type encountered: " + resp.getRuleResponsibilityType());
397 }
398 ActionRequestValue actionRequest;
399 if (parentRequest == null) {
400 actionRequest = arFactory.addRootActionRequest(resp.getActionRequestedCd(),
401 resp.getPriority(),
402 recipient,
403 rule.getDescription(),
404 resp.getResponsibilityId(),
405 rule.getForceAction(),
406 resp.getApprovePolicy(),
407 rule.getRuleBaseValuesId());
408
409
410
411
412
413
414
415
416
417
418
419
420
421 List<RuleDelegation> ruleDelegations = getRuleDelegationService().findByResponsibilityId(resp.getResponsibilityId());
422 if (ruleDelegations != null && !ruleDelegations.isEmpty()) {
423 for (RuleDelegation childRuleDelegation : ruleDelegations) {
424 buildDelegationGraph(arFactory, context, childRuleDelegation.getDelegationRuleBaseValues(), routeHeader, actionRequest, childRuleDelegation);
425 }
426 }
427
428 } else {
429 arFactory.addDelegationRequest(parentRequest, recipient, resp.getResponsibilityId(), rule.getForceAction(), ruleDelegation.getDelegationType(), rule.getDescription(), rule.getRuleBaseValuesId());
430 }
431 }
432
433 private boolean isDuplicateActionRequestDetected(DocumentRouteHeaderValue routeHeader, RouteNodeInstance nodeInstance, RuleResponsibility resp, String qualifiedRoleName) {
434 List<ActionRequestValue> requests = getActionRequestService().findByStatusAndDocId(KEWConstants.ACTION_REQUEST_DONE_STATE, routeHeader.getRouteHeaderId());
435 for (ActionRequestValue request : requests)
436 {
437 if (((nodeInstance != null && request.getNodeInstance() != null && request.getNodeInstance().getRouteNodeInstanceId().equals(nodeInstance.getRouteNodeInstanceId())) || request
438 .getRouteLevel().equals(routeHeader.getDocRouteLevel()))
439 && request.getResponsibilityId().equals(resp.getResponsibilityId()) && ObjectUtils.equals(request.getQualifiedRoleName(), qualifiedRoleName))
440 {
441 return true;
442 }
443 }
444 return false;
445 }
446
447 public RuleService getRuleService() {
448 return KEWServiceLocator.getRuleService();
449 }
450
451 public RuleDelegationService getRuleDelegationService() {
452 return KEWServiceLocator.getRuleDelegationService();
453 }
454
455
456 private ActionRequestService getActionRequestService() {
457 return KEWServiceLocator.getActionRequestService();
458 }
459
460 public int getNumberOfMatchingRules() {
461 return selectedRules;
462 }
463
464
465
466
467
468
469
470
471 }