View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.engine.node;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.kew.api.KewApiConstants;
21  import org.kuali.rice.kew.api.doctype.RouteNodeConfigurationParameterContract;
22  import org.kuali.rice.kew.api.doctype.RouteNodeContract;
23  import org.kuali.rice.kew.api.exception.ResourceUnavailableException;
24  import org.kuali.rice.kew.doctype.bo.DocumentType;
25  import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
26  import org.kuali.rice.kew.rule.service.RuleTemplateService;
27  import org.kuali.rice.kew.service.KEWServiceLocator;
28  import org.kuali.rice.kew.util.Utilities;
29  import org.kuali.rice.kim.api.group.Group;
30  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
31  import org.kuali.rice.krad.data.jpa.converters.Boolean01BigDecimalConverter;
32  import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
33  
34  import javax.persistence.CascadeType;
35  import javax.persistence.Column;
36  import javax.persistence.Convert;
37  import javax.persistence.Entity;
38  import javax.persistence.FetchType;
39  import javax.persistence.GeneratedValue;
40  import javax.persistence.Id;
41  import javax.persistence.JoinColumn;
42  import javax.persistence.JoinTable;
43  import javax.persistence.ManyToMany;
44  import javax.persistence.ManyToOne;
45  import javax.persistence.OneToMany;
46  import javax.persistence.OneToOne;
47  import javax.persistence.Table;
48  import javax.persistence.Transient;
49  import javax.persistence.Version;
50  import java.io.Serializable;
51  import java.util.ArrayList;
52  import java.util.List;
53  import java.util.Map;
54  
55  /**
56   * Represents the prototype definition of a node in the route path of {@link DocumentType}.
57   *
58   * @author Kuali Rice Team (rice.collab@kuali.org)
59   */
60  @Entity
61  @Table(name="KREW_RTE_NODE_T")
62  public class RouteNode implements Serializable, RouteNodeContract {    
63  
64      private static final long serialVersionUID = 4891233177051752726L;
65  
66      public static final String CONTENT_FRAGMENT_CFG_KEY = "contentFragment";
67      public static final String RULE_SELECTOR_CFG_KEY = "ruleSelector";
68  
69      @Id
70      @PortableSequenceGenerator(name = "KREW_RTE_NODE_S")
71      @GeneratedValue(generator = "KREW_RTE_NODE_S")
72  	@Column(name = "RTE_NODE_ID", nullable = false)
73  	private String routeNodeId;
74  
75      @Column(name = "DOC_TYP_ID", insertable = false, updatable = false)
76  	private String documentTypeId;
77  
78      @ManyToOne
79      @JoinColumn(name="DOC_TYP_ID", nullable = false)
80      private DocumentType documentType;
81  
82      @Column(name="NM", nullable = false)
83  	private String routeNodeName;
84  
85      @Column(name="RTE_MTHD_NM")
86  	private String routeMethodName;
87  
88      @Column(name="FNL_APRVR_IND")
89      @Convert(converter=Boolean01BigDecimalConverter.class)
90  	private Boolean finalApprovalInd;
91  
92      @Column(name="MNDTRY_RTE_IND")
93      @Convert(converter=Boolean01BigDecimalConverter.class)
94  	private Boolean mandatoryRouteInd;
95  
96      @Column(name="GRP_ID")
97  	private String exceptionWorkgroupId;
98  
99      @Column(name="RTE_MTHD_CD")
100 	private String routeMethodCode;
101 
102     @Column(name="ACTVN_TYP")
103     private String activationType = ActivationTypeEnum.PARALLEL.getCode();
104 
105     /**
106      * The nextDocStatus property represents the value of the ApplicationDocumentStatus to be set 
107      * in the RouteHeader upon transitioning from this node.
108      */
109     @Column(name="NEXT_DOC_STAT")
110 	private String nextDocStatus;
111 
112     @Version
113 	@Column(name="VER_NBR")
114 	private Integer lockVerNbr;
115 
116     @Column(name="TYP")
117     private String nodeType = RequestsNode.class.getName();
118 
119     @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "nextNodes")
120     private List<RouteNode> previousNodes = new ArrayList<RouteNode>();
121 
122     @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
123     @JoinTable(name = "KREW_RTE_NODE_LNK_T", joinColumns = @JoinColumn(name = "FROM_RTE_NODE_ID"), inverseJoinColumns = @JoinColumn(name = "TO_RTE_NODE_ID"))
124     private List<RouteNode> nextNodes = new ArrayList<RouteNode>();
125 
126     @OneToMany(fetch = FetchType.EAGER, mappedBy = "routeNode", cascade = CascadeType.ALL)
127     private List<RouteNodeConfigParam> configParams  = new ArrayList<RouteNodeConfigParam>(0);
128 
129     @OneToOne(cascade = CascadeType.ALL)
130     @JoinColumn(name = "BRCH_PROTO_ID")
131     private BranchPrototype branch;
132 
133     @Transient private String exceptionWorkgroupName;
134     @Transient private RuleTemplateBo ruleTemplate;
135 
136     /**
137     * Looks up a config parameter for this route node definition
138     * @param key the config param key
139     * @return the RouteNodeConfigParam if present
140     */
141     protected RouteNodeConfigParam getConfigParam(String key) {
142         Map<String, RouteNodeConfigParam> configParamMap = Utilities.getKeyValueCollectionAsLookupTable(configParams);
143         return configParamMap.get(key);
144     }
145 
146     /**
147      * Sets a config parameter for this route node definition.  If the key already exists
148      * the existing RouteNodeConfigParam is modified, otherwise a new one is created
149      * @param key the key of the parameter to set
150      * @param value the value to set
151      */
152     protected void setConfigParam(String key, String value) {
153         Map<String, RouteNodeConfigParam> configParamMap = Utilities.getKeyValueCollectionAsLookupTable(configParams);
154         RouteNodeConfigParam cfCfgParam = configParamMap.get(key);
155         if (cfCfgParam == null) {
156             cfCfgParam = new RouteNodeConfigParam(this, key, value);
157             configParams.add(cfCfgParam);
158         } else {
159             cfCfgParam.setValue(value);
160         }
161     }
162 
163     public List<RouteNodeConfigParam> getConfigParams() {
164         return configParams;
165     }
166 
167     public void setConfigParams(List<RouteNodeConfigParam> configParams) {
168         this.configParams = configParams;
169     }
170 
171     /**
172      * @return the RouteNodeConfigParam value under the 'contentFragment'  key
173      */
174     public String getContentFragment() {
175         RouteNodeConfigParam cfCfgParam = getConfigParam(CONTENT_FRAGMENT_CFG_KEY);
176         if (cfCfgParam == null) return null;
177         return cfCfgParam.getValue();
178     }
179 
180     /**
181      * @param contentFragment the content fragment of the node, which will be set as a RouteNodeConfigParam under the 'contentFragment' key
182      */
183     public void setContentFragment(String contentFragment) {
184         setConfigParam(CONTENT_FRAGMENT_CFG_KEY, contentFragment);
185     }
186 
187     public String getActivationType() {
188         return activationType;
189     }
190 
191     public void setActivationType(String activationType) {
192         /* Cleanse the input.
193          * This is surely not the best way to validate the activation types;
194          * it would probably be better to use typesafe enums accross the board
195          * but that would probably entail refactoring large swaths of code, not
196          * to mention reconfiguring OJB (can typesafe enums be used?) and dealing
197          * with serialization compatibility issues (if any).
198          * So instead, let's just be sure to fail-fast.
199          */
200         ActivationTypeEnum at = ActivationTypeEnum.lookupCode(activationType);
201         this.activationType = at.getCode();
202     }
203 
204     public Group getExceptionWorkgroup() {
205     	if (!StringUtils.isBlank(exceptionWorkgroupId)) {
206     		return KimApiServiceLocator.getGroupService().getGroup(exceptionWorkgroupId);
207     	}
208     	return null;
209     }
210     
211     public boolean isExceptionGroupDefined() {
212     	return getExceptionWorkgroupId() != null;
213     }
214 
215     public String getExceptionWorkgroupId() {
216         return exceptionWorkgroupId;
217     }
218 
219     public void setExceptionWorkgroupId(String workgroupId) {
220         this.exceptionWorkgroupId = workgroupId;
221     }
222 
223     public void setFinalApprovalInd(Boolean finalApprovalInd) {
224         this.finalApprovalInd = finalApprovalInd;
225     }
226 
227     public void setMandatoryRouteInd(Boolean mandatoryRouteInd) {
228         this.mandatoryRouteInd = mandatoryRouteInd;
229     }
230 
231     public String getRouteMethodName() {
232         return routeMethodName;
233     }
234 
235     public void setRouteMethodName(String routeMethodName) {
236         this.routeMethodName = routeMethodName;
237     }
238 
239     public String getDocumentTypeId() {
240         return getDocumentType() != null ? getDocumentType().getDocumentTypeId() : documentTypeId;
241     }
242 
243     public void setDocumentTypeId(String documentTypeId) {
244         this.documentTypeId = documentTypeId;
245     }
246 
247     public String getRouteNodeId() {
248         return routeNodeId;
249     }
250 
251     public void setRouteNodeId(String routeNodeId) {
252         this.routeNodeId = routeNodeId;
253     }
254 
255     public String getRouteNodeName() {
256         return routeNodeName;
257     }
258 
259     public void setRouteNodeName(String routeLevelName) {
260         this.routeNodeName = routeLevelName;
261     }
262 
263     public DocumentType getDocumentType() {
264         return documentType;
265     }
266 
267     public void setDocumentType(DocumentType documentType) {
268         this.documentType = documentType;
269     }
270 
271     public String getRouteMethodCode() {
272         return routeMethodCode;
273     }
274 
275     public void setRouteMethodCode(String routeMethodCode) {
276         this.routeMethodCode = routeMethodCode;
277     }
278 
279 	/**
280 	 * @param nextDocStatus the nextDocStatus to set
281 	 */
282 	public void setNextDocStatus(String nextDocStatus) {
283 		this.nextDocStatus = nextDocStatus;
284 	}
285 
286 	/**
287 	 * @return the nextDocStatus
288 	 */
289 	public String getNextDocStatus() {
290 		return nextDocStatus;
291 	}
292 	
293     public String getExceptionWorkgroupName() {
294     	Group exceptionGroup = getExceptionWorkgroup();
295         if (exceptionWorkgroupName == null || exceptionWorkgroupName.equals("")) {
296             if (exceptionGroup != null) {
297                 return exceptionGroup.getName();
298             }
299         }
300         return exceptionWorkgroupName;
301     }
302 
303     public void setExceptionWorkgroupName(String exceptionWorkgroupName) {
304         this.exceptionWorkgroupName = exceptionWorkgroupName;
305     }
306 
307     public Integer getLockVerNbr() {
308         return lockVerNbr;
309     }
310 
311     public void setLockVerNbr(Integer lockVerNbr) {
312         this.lockVerNbr = lockVerNbr;
313     }
314 
315     public boolean isFlexRM() {
316         return routeMethodCode != null && routeMethodCode.equals(KewApiConstants.ROUTE_LEVEL_FLEX_RM);
317     }
318 
319     public boolean isRulesEngineNode() {
320         return StringUtils.equals(routeMethodCode, KewApiConstants.ROUTE_LEVEL_RULES_ENGINE);
321     }
322 
323     public boolean isPeopleFlowNode() {
324         return StringUtils.equals(routeMethodCode, KewApiConstants.ROUTE_LEVEL_PEOPLE_FLOW);
325     }
326     
327     public boolean isRoleNode() {
328     	try {
329     		return nodeType != null && NodeType.fromNode(this).isTypeOf(NodeType.ROLE);
330     	} catch( ResourceUnavailableException ex ) {
331     		Logger.getLogger( RouteNode.class ).info( "isRoleNode(): Unable to determine node type: " + ex.getMessage() );
332     		return false;
333     	}
334     }
335 
336     public Boolean getFinalApprovalInd() {
337         return finalApprovalInd;
338     }
339 
340     public Boolean getMandatoryRouteInd() {
341         return mandatoryRouteInd;
342     }
343 
344     public void addNextNode(RouteNode nextNode) {
345         getNextNodes().add(nextNode);
346         nextNode.getPreviousNodes().add(this);
347     }
348 
349     public List<RouteNode> getNextNodes() {
350         return nextNodes;
351     }
352 
353     public void setNextNodes(List<RouteNode> nextNodes) {
354         this.nextNodes = nextNodes;
355     }
356 
357     public List<RouteNode> getPreviousNodes() {
358         return previousNodes;
359     }
360 
361     public void setPreviousNodes(List<RouteNode> parentNodes) {
362         this.previousNodes = parentNodes;
363     }
364 
365     public RuleTemplateBo getRuleTemplate() {
366         if (ruleTemplate == null) {
367             RuleTemplateService ruleTemplateService = (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
368             ruleTemplate = ruleTemplateService.findByRuleTemplateName(getRouteMethodName());
369         }
370         return ruleTemplate;
371     }
372 
373     public String getNodeType() {
374         return nodeType;
375     }
376 
377     public void setNodeType(String nodeType) {
378         this.nodeType = nodeType;
379     }
380 
381     public BranchPrototype getBranch() {
382         return branch;
383     }
384 
385     public void setBranch(BranchPrototype branch) {
386         this.branch = branch;
387     }
388 
389 	/**
390 	 * This overridden method ...
391 	 * 
392 	 * @see java.lang.Object#toString()
393 	 */
394 	@Override
395 	public String toString() {
396 		return "RouteNode[routeNodeName="+routeNodeName+", nodeType="+nodeType+", activationType="+activationType+"]";
397 	}
398 
399     @Override
400     public Long getVersionNumber() {
401         if (lockVerNbr == null) {
402             return null;
403         }
404         return Long.valueOf(lockVerNbr.longValue());
405     }
406 
407     @Override
408     public String getId() {
409         if (routeNodeId == null) {
410             return null;
411         }
412         return routeNodeId.toString();
413     }
414 
415     @Override
416     public String getName() {
417         return getRouteNodeName();
418     }
419 
420     @Override
421     public boolean isFinalApproval() {
422         if (finalApprovalInd == null) {
423             return false;
424         }
425         return finalApprovalInd.booleanValue();
426     }
427 
428     @Override
429     public boolean isMandatory() {
430         if (mandatoryRouteInd == null) {
431             return false;
432         }
433         return mandatoryRouteInd.booleanValue();
434     }
435 
436     @Override
437     public String getExceptionGroupId() {
438         return exceptionWorkgroupId;
439     }
440 
441     @Override
442     public String getType() {
443         return nodeType;
444     }
445 
446     @Override
447     public String getBranchName() {
448         if (branch == null) {
449             return null;
450         }
451         return branch.getName();
452     }
453 
454     @Override
455     public String getNextDocumentStatus() {
456         return nextDocStatus;
457     }
458 
459     @Override
460     public List<? extends RouteNodeConfigurationParameterContract> getConfigurationParameters() {
461         return configParams;
462     }
463 
464     @Override
465     public List<String> getPreviousNodeIds() {
466         List<String> previousNodeIds = new ArrayList<String>();
467         if (previousNodes != null) {
468             for (RouteNode previousNode : previousNodes) {
469                 previousNodeIds.add(previousNode.getRouteNodeId().toString());
470             }
471         }
472         return previousNodeIds;
473     }
474 
475     @Override
476     public List<String> getNextNodeIds() {
477         List<String> nextNodeIds = new ArrayList<String>();
478         if (nextNodeIds != null) {
479             for (RouteNode nextNode : nextNodes) {
480                 nextNodeIds.add(nextNode.getRouteNodeId().toString());
481             }
482         }
483         return nextNodeIds;
484     }
485 	
486 
487 }