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 }