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