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 }