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 return null;
225 }
226
227 private void makeActionRequests(ActionRequestFactory arFactory, RouteContext context, org.kuali.rice.kew.api.rule.Rule rule, DocumentRouteHeaderValue routeHeader, ActionRequestValue parentRequest, RuleDelegation ruleDelegation)
228 throws WorkflowException {
229
230 List<org.kuali.rice.kew.api.rule.RuleResponsibility> responsibilities = rule.getRuleResponsibilities();
231 makeActionRequests(arFactory, responsibilities, context, rule, routeHeader, parentRequest, ruleDelegation);
232 }
233
234 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) {
235
236
237 for (org.kuali.rice.kew.api.rule.RuleResponsibility responsibility : responsibilities)
238 {
239
240
241 if (responsibility.isUsingRole())
242 {
243 makeRoleActionRequests(arFactory, context, rule, responsibility, routeHeader, parentRequest, ruleDelegation);
244 } else
245 {
246 makeActionRequest(arFactory, context, rule, routeHeader, responsibility, parentRequest, ruleDelegation);
247 }
248
249
250
251 }
252 }
253
254 private void buildDelegationGraph(ActionRequestFactory arFactory, RouteContext context,
255 org.kuali.rice.kew.api.rule.Rule delegationRule, DocumentRouteHeaderValue routeHeaderValue, ActionRequestValue parentRequest, RuleDelegation ruleDelegation) {
256 context.setActionRequest(parentRequest);
257 RuleBaseValues delRuleBo = KEWServiceLocator.getRuleService().getRuleByName(delegationRule.getName());
258 if (delegationRule.isActive()) {
259 for (org.kuali.rice.kew.api.rule.RuleResponsibility delegationResp : delegationRule.getRuleResponsibilities())
260 {
261 if (delegationResp.isUsingRole())
262 {
263 makeRoleActionRequests(arFactory, context, delegationRule, delegationResp, routeHeaderValue, parentRequest, ruleDelegation);
264 } else if (delRuleBo.isMatch(context.getDocumentContent()))
265 {
266 makeActionRequest(arFactory, context, delegationRule, routeHeaderValue, delegationResp, parentRequest, ruleDelegation);
267 }
268 }
269 }
270 }
271
272
273
274
275 private void makeRoleActionRequests(ActionRequestFactory arFactory, RouteContext context,
276 org.kuali.rice.kew.api.rule.Rule rule, org.kuali.rice.kew.api.rule.RuleResponsibility resp, DocumentRouteHeaderValue routeHeader, ActionRequestValue parentRequest,
277 RuleDelegation ruleDelegation)
278 {
279 String roleName = resp.getResolvedRoleName();
280
281 RoleAttribute roleAttribute = null;
282 if (resp.isUsingRole()) {
283
284 roleAttribute = (RoleAttribute) GlobalResourceLoader.getResourceLoader().getObject(new ObjectDefinition(
285 resp.getRoleAttributeName()));
286
287 if (roleAttribute instanceof XmlConfiguredAttribute) {
288 ExtensionDefinition roleAttributeDefinition = null;
289 for (RuleTemplateAttribute ruleTemplateAttribute : rule.getRuleTemplate().getRuleTemplateAttributes()) {
290 if (resp.getRoleAttributeName().equals(ruleTemplateAttribute.getRuleAttribute().getResourceDescriptor())) {
291 roleAttributeDefinition = ruleTemplateAttribute.getRuleAttribute();
292 break;
293 }
294 }
295 ((XmlConfiguredAttribute)roleAttribute).setExtensionDefinition(roleAttributeDefinition);
296 }
297 }
298
299 List<String> qualifiedRoleNames = new ArrayList<String>();
300 if (parentRequest != null && parentRequest.getQualifiedRoleName() != null) {
301 qualifiedRoleNames.add(parentRequest.getQualifiedRoleName());
302 } else {
303 qualifiedRoleNames.addAll(roleAttribute.getQualifiedRoleNames(roleName, context.getDocumentContent()));
304 }
305 for (String qualifiedRoleName : qualifiedRoleNames) {
306 if (parentRequest == null && isDuplicateActionRequestDetected(routeHeader, context.getNodeInstance(), resp, qualifiedRoleName)) {
307 continue;
308 }
309
310 ResolvedQualifiedRole resolvedRole = roleAttribute.resolveQualifiedRole(context, roleName, qualifiedRoleName);
311 RoleRecipient recipient = new RoleRecipient(roleName, qualifiedRoleName, resolvedRole);
312 if (parentRequest == null) {
313 ActionRequestValue roleRequest = arFactory.addRoleRequest(recipient, resp.getActionRequestedCd(),
314 resp.getApprovePolicy(), resp.getPriority(), resp.getResponsibilityId(), rule.isForceAction(),
315 rule.getDescription(), rule.getId());
316
317 List<RuleDelegation> ruleDelegations = getRuleService().getRuleDelegationsByResponsibiltityId(resp.getResponsibilityId());
318 if (ruleDelegations != null && !ruleDelegations.isEmpty()) {
319
320 for (ActionRequestValue request : roleRequest.getChildrenRequests()) {
321 for (RuleDelegation childRuleDelegation : ruleDelegations) {
322 buildDelegationGraph(arFactory, context, childRuleDelegation.getDelegationRule(), routeHeader, request, childRuleDelegation);
323 }
324 }
325 }
326
327 } else {
328 arFactory.addDelegationRoleRequest(parentRequest, resp.getApprovePolicy(), recipient, resp.getResponsibilityId(), rule.isForceAction(), ruleDelegation.getDelegationType(), rule.getDescription(), rule.getId());
329 }
330 }
331 }
332
333
334
335
336
337
338
339
340
341
342
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 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,
374 RuleDelegation ruleDelegation) {
375 if (parentRequest == null && isDuplicateActionRequestDetected(routeHeader, context.getNodeInstance(), resp, null)) {
376 return;
377 }
378 Recipient recipient;
379 if (resp.isUsingPrincipal()) {
380 recipient = new KimPrincipalRecipient(resp.getPrincipalId());
381 } else if (resp.isUsingGroup()) {
382 recipient = new KimGroupRecipient(resp.getGroupId());
383 } else {
384 throw new RiceRuntimeException("Illegal rule responsibility type encountered");
385 }
386 ActionRequestValue actionRequest;
387 if (parentRequest == null) {
388 actionRequest = arFactory.addRootActionRequest(resp.getActionRequestedCd(),
389 resp.getPriority(),
390 recipient,
391 rule.getDescription(),
392 resp.getResponsibilityId(),
393 rule.isForceAction(),
394 resp.getApprovePolicy(),
395 rule.getId());
396
397 List<RuleDelegation> ruleDelegations = getRuleService().getRuleDelegationsByResponsibiltityId(
398 resp.getResponsibilityId());
399 if (ruleDelegations != null && !ruleDelegations.isEmpty()) {
400 for (RuleDelegation childRuleDelegation : ruleDelegations) {
401 buildDelegationGraph(arFactory, context, childRuleDelegation.getDelegationRule(), routeHeader, actionRequest, childRuleDelegation);
402 }
403 }
404
405 } else {
406 arFactory.addDelegationRequest(parentRequest, recipient, resp.getResponsibilityId(), rule.isForceAction(), ruleDelegation.getDelegationType(), rule.getDescription(), rule.getId());
407 }
408 }
409
410 private boolean isDuplicateActionRequestDetected(DocumentRouteHeaderValue routeHeader, RouteNodeInstance nodeInstance, org.kuali.rice.kew.api.rule.RuleResponsibility resp, String qualifiedRoleName) {
411 List<ActionRequestValue> requests = getActionRequestService().findByStatusAndDocId(ActionRequestStatus.DONE.getCode(), routeHeader.getDocumentId());
412 for (ActionRequestValue request : requests)
413 {
414 if (((nodeInstance != null
415 && request.getNodeInstance() != null
416 && request.getNodeInstance().getRouteNodeInstanceId().equals(nodeInstance.getRouteNodeInstanceId())
417 ) || request.getRouteLevel().equals(routeHeader.getDocRouteLevel())
418 )
419 && request.getResponsibilityId().equals(resp.getResponsibilityId())
420 && ObjectUtils.equals(request.getQualifiedRoleName(), qualifiedRoleName)) {
421 return true;
422 }
423 }
424 return false;
425 }
426
427 public RuleService getRuleService() {
428 return KewApiServiceLocator.getRuleService();
429 }
430
431 private ActionRequestService getActionRequestService() {
432 return KEWServiceLocator.getActionRequestService();
433 }
434
435 public int getNumberOfMatchingRules() {
436 return selectedRules;
437 }
438
439 }