001 /**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.kew.engine.node;
017
018 import java.io.Serializable;
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.Map;
022
023 import javax.persistence.CascadeType;
024 import javax.persistence.Column;
025 import javax.persistence.Entity;
026 import javax.persistence.FetchType;
027 import javax.persistence.GeneratedValue;
028 import javax.persistence.Id;
029 import javax.persistence.JoinColumn;
030 import javax.persistence.JoinTable;
031 import javax.persistence.ManyToMany;
032 import javax.persistence.ManyToOne;
033 import javax.persistence.NamedQueries;
034 import javax.persistence.NamedQuery;
035 import javax.persistence.OneToMany;
036 import javax.persistence.OneToOne;
037 import javax.persistence.Table;
038 import javax.persistence.Transient;
039 import javax.persistence.Version;
040
041 import org.apache.commons.lang.StringUtils;
042 import org.apache.log4j.Logger;
043 import org.hibernate.annotations.Fetch;
044 import org.hibernate.annotations.FetchMode;
045 import org.hibernate.annotations.GenericGenerator;
046 import org.hibernate.annotations.Parameter;
047 import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
048 import org.kuali.rice.kew.api.doctype.RouteNodeConfigurationParameterContract;
049 import org.kuali.rice.kew.api.doctype.RouteNodeContract;
050 import org.kuali.rice.kew.api.exception.ResourceUnavailableException;
051 import org.kuali.rice.kew.doctype.bo.DocumentType;
052 import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
053 import org.kuali.rice.kew.rule.service.RuleTemplateService;
054 import org.kuali.rice.kew.service.KEWServiceLocator;
055 import org.kuali.rice.kew.api.KewApiConstants;
056 import org.kuali.rice.kew.util.Utilities;
057 import org.kuali.rice.kim.api.group.Group;
058 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
059
060 /**
061 * Represents the prototype definition of a node in the route path of {@link DocumentType}.
062 *
063 * @author Kuali Rice Team (rice.collab@kuali.org)
064 */
065 @Entity
066 @Table(name="KREW_RTE_NODE_T")
067 //@Sequence(name="KREW_RTE_NODE_S", property="routeNodeId")
068 @NamedQueries({
069 @NamedQuery(name="RouteNode.FindByRouteNodeId",query="select r from RouteNode as r where r.routeNodeId = :routeNodeId"),
070 @NamedQuery(name="RouteNode.FindRouteNodeByName", query="select r from RouteNode as r where r.routeNodeName = :routeNodeName and r.documentTypeId = :documentTypeId"),
071 @NamedQuery(name="RouteNode.FindApprovalRouteNodes", query="select r from RouteNode as r where r.documentTypeId = :documentTypeId and r.finalApprovalInd = :finalApprovalInd")
072 })
073 public class RouteNode implements Serializable, RouteNodeContract {
074
075 private static final long serialVersionUID = 4891233177051752726L;
076
077 public static final String CONTENT_FRAGMENT_CFG_KEY = "contentFragment";
078 public static final String RULE_SELECTOR_CFG_KEY = "ruleSelector";
079
080 @Id
081 @GeneratedValue(generator="KREW_RTE_NODE_S")
082 @GenericGenerator(name="KREW_RTE_NODE_S",strategy="org.hibernate.id.enhanced.SequenceStyleGenerator",parameters={
083 @Parameter(name="sequence_name",value="KREW_RTE_NODE_S"),
084 @Parameter(name="value_column",value="id")
085 })
086 @Column(name="RTE_NODE_ID")
087 private String routeNodeId;
088 @Column(name="DOC_TYP_ID",insertable=false, updatable=false)
089 private String documentTypeId;
090 @Column(name="NM")
091 private String routeNodeName;
092 @Column(name="RTE_MTHD_NM")
093 private String routeMethodName;
094 @Column(name="FNL_APRVR_IND")
095 private Boolean finalApprovalInd;
096 @Column(name="MNDTRY_RTE_IND")
097 private Boolean mandatoryRouteInd;
098 @Column(name="GRP_ID")
099 private String exceptionWorkgroupId;
100 @Column(name="RTE_MTHD_CD")
101 private String routeMethodCode;
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 @ManyToOne(fetch=FetchType.EAGER)
116 @JoinColumn(name="DOC_TYP_ID")
117 private DocumentType documentType;
118 @Transient
119 private String exceptionWorkgroupName;
120
121 @Transient
122 private RuleTemplateBo ruleTemplate;
123 @Column(name="TYP")
124 private String nodeType = RequestsNode.class.getName();
125
126 //@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST}, mappedBy="nextNodes")
127 @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST}, mappedBy="nextNodes")
128 @Fetch(value = FetchMode.SELECT)
129 //@JoinTable(name = "KREW_RTE_NODE_LNK_T", joinColumns = @JoinColumn(name = "TO_RTE_NODE_ID"), inverseJoinColumns = @JoinColumn(name = "FROM_RTE_NODE_ID"))
130 private List<RouteNode> previousNodes = new ArrayList<RouteNode>();
131 //@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
132 @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST})
133 @Fetch(value = FetchMode.SELECT)
134 @JoinTable(name = "KREW_RTE_NODE_LNK_T", joinColumns = @JoinColumn(name = "FROM_RTE_NODE_ID"), inverseJoinColumns = @JoinColumn(name = "TO_RTE_NODE_ID"))
135 private List<RouteNode> nextNodes = new ArrayList<RouteNode>();
136 @OneToOne(fetch=FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE})
137 @JoinColumn(name="BRCH_PROTO_ID")
138 private BranchPrototype branch;
139 @OneToMany(fetch=FetchType.EAGER,mappedBy="routeNode",cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
140 @Fetch(value = FetchMode.SELECT)
141 private List<RouteNodeConfigParam> configParams = new ArrayList<RouteNodeConfigParam>(0);
142
143 /**
144 * Looks up a config parameter for this route node definition
145 * @param key the config param key
146 * @return the RouteNodeConfigParam if present
147 */
148 protected RouteNodeConfigParam getConfigParam(String key) {
149 Map<String, RouteNodeConfigParam> configParamMap = Utilities.getKeyValueCollectionAsLookupTable(configParams);
150 return configParamMap.get(key);
151 }
152
153 /**
154 * Sets a config parameter for this route node definition. If the key already exists
155 * the existing RouteNodeConfigParam is modified, otherwise a new one is created
156 * @param key the key of the parameter to set
157 * @param value the value to set
158 */
159 protected void setConfigParam(String key, String value) {
160 Map<String, RouteNodeConfigParam> configParamMap = Utilities.getKeyValueCollectionAsLookupTable(configParams);
161 RouteNodeConfigParam cfCfgParam = configParamMap.get(key);
162 if (cfCfgParam == null) {
163 cfCfgParam = new RouteNodeConfigParam(this, key, value);
164 configParams.add(cfCfgParam);
165 } else {
166 cfCfgParam.setValue(value);
167 }
168 }
169
170 public List<RouteNodeConfigParam> getConfigParams() {
171 return configParams;
172 }
173
174 public void setConfigParams(List<RouteNodeConfigParam> configParams) {
175 this.configParams = configParams;
176 }
177
178 /**
179 * @return the RouteNodeConfigParam value under the 'contentFragment' key
180 */
181 public String getContentFragment() {
182 RouteNodeConfigParam cfCfgParam = getConfigParam(CONTENT_FRAGMENT_CFG_KEY);
183 if (cfCfgParam == null) return null;
184 return cfCfgParam.getValue();
185 }
186
187 /**
188 * @param contentFragment the content fragment of the node, which will be set as a RouteNodeConfigParam under the 'contentFragment' key
189 */
190 public void setContentFragment(String contentFragment) {
191 setConfigParam(CONTENT_FRAGMENT_CFG_KEY, contentFragment);
192 }
193
194 public String getActivationType() {
195 return activationType;
196 }
197
198 public void setActivationType(String activationType) {
199 /* Cleanse the input.
200 * This is surely not the best way to validate the activation types;
201 * it would probably be better to use typesafe enums accross the board
202 * but that would probably entail refactoring large swaths of code, not
203 * to mention reconfiguring OJB (can typesafe enums be used?) and dealing
204 * with serialization compatibility issues (if any).
205 * So instead, let's just be sure to fail-fast.
206 */
207 ActivationTypeEnum at = ActivationTypeEnum.lookupCode(activationType);
208 this.activationType = at.getCode();
209 }
210
211 public Group getExceptionWorkgroup() {
212 if (!StringUtils.isBlank(exceptionWorkgroupId)) {
213 return KimApiServiceLocator.getGroupService().getGroup(exceptionWorkgroupId);
214 }
215 return null;
216 }
217
218 public boolean isExceptionGroupDefined() {
219 return getExceptionWorkgroupId() != null;
220 }
221
222 public String getExceptionWorkgroupId() {
223 return exceptionWorkgroupId;
224 }
225
226 public void setExceptionWorkgroupId(String workgroupId) {
227 this.exceptionWorkgroupId = workgroupId;
228 }
229
230 public void setFinalApprovalInd(Boolean finalApprovalInd) {
231 this.finalApprovalInd = finalApprovalInd;
232 }
233
234 public void setMandatoryRouteInd(Boolean mandatoryRouteInd) {
235 this.mandatoryRouteInd = mandatoryRouteInd;
236 }
237
238 public String getRouteMethodName() {
239 return routeMethodName;
240 }
241
242 public void setRouteMethodName(String routeMethodName) {
243 this.routeMethodName = routeMethodName;
244 }
245
246 public String getDocumentTypeId() {
247 return documentTypeId;
248 }
249
250 public void setDocumentTypeId(String documentTypeId) {
251 this.documentTypeId = documentTypeId;
252 }
253
254 public String getRouteNodeId() {
255 return routeNodeId;
256 }
257
258 public void setRouteNodeId(String routeNodeId) {
259 this.routeNodeId = routeNodeId;
260 }
261
262 public String getRouteNodeName() {
263 return routeNodeName;
264 }
265
266 public void setRouteNodeName(String routeLevelName) {
267 this.routeNodeName = routeLevelName;
268 }
269
270 public DocumentType getDocumentType() {
271 return documentType;
272 }
273
274 public void setDocumentType(DocumentType documentType) {
275 this.documentType = documentType;
276 }
277
278 public String getRouteMethodCode() {
279 return routeMethodCode;
280 }
281
282 public void setRouteMethodCode(String routeMethodCode) {
283 this.routeMethodCode = routeMethodCode;
284 }
285
286 /**
287 * @param nextDocStatus the nextDocStatus to set
288 */
289 public void setNextDocStatus(String nextDocStatus) {
290 this.nextDocStatus = nextDocStatus;
291 }
292
293 /**
294 * @return the nextDocStatus
295 */
296 public String getNextDocStatus() {
297 return nextDocStatus;
298 }
299
300 public String getExceptionWorkgroupName() {
301 Group exceptionGroup = getExceptionWorkgroup();
302 if (exceptionWorkgroupName == null || exceptionWorkgroupName.equals("")) {
303 if (exceptionGroup != null) {
304 return exceptionGroup.getName();
305 }
306 }
307 return exceptionWorkgroupName;
308 }
309
310 public void setExceptionWorkgroupName(String exceptionWorkgroupName) {
311 this.exceptionWorkgroupName = exceptionWorkgroupName;
312 }
313
314 public Integer getLockVerNbr() {
315 return lockVerNbr;
316 }
317
318 public void setLockVerNbr(Integer lockVerNbr) {
319 this.lockVerNbr = lockVerNbr;
320 }
321
322 public boolean isFlexRM() {
323 return routeMethodCode != null && routeMethodCode.equals(KewApiConstants.ROUTE_LEVEL_FLEX_RM);
324 }
325
326 public boolean isRulesEngineNode() {
327 return StringUtils.equals(routeMethodCode, KewApiConstants.ROUTE_LEVEL_RULES_ENGINE);
328 }
329
330 public boolean isPeopleFlowNode() {
331 return StringUtils.equals(routeMethodCode, KewApiConstants.ROUTE_LEVEL_PEOPLE_FLOW);
332 }
333
334 public boolean isRoleNode() {
335 try {
336 return nodeType != null && NodeType.fromNode(this).isTypeOf(NodeType.ROLE);
337 } catch( ResourceUnavailableException ex ) {
338 Logger.getLogger( RouteNode.class ).info( "isRoleNode(): Unable to determine node type: " + ex.getMessage() );
339 return false;
340 }
341 }
342
343 public Boolean getFinalApprovalInd() {
344 return finalApprovalInd;
345 }
346
347 public Boolean getMandatoryRouteInd() {
348 return mandatoryRouteInd;
349 }
350
351 public void addNextNode(RouteNode nextNode) {
352 getNextNodes().add(nextNode);
353 nextNode.getPreviousNodes().add(this);
354 }
355
356 public List<RouteNode> getNextNodes() {
357 return nextNodes;
358 }
359
360 public void setNextNodes(List<RouteNode> nextNodes) {
361 this.nextNodes = nextNodes;
362 }
363
364 public List<RouteNode> getPreviousNodes() {
365 return previousNodes;
366 }
367
368 public void setPreviousNodes(List<RouteNode> parentNodes) {
369 this.previousNodes = parentNodes;
370 }
371
372 public RuleTemplateBo getRuleTemplate() {
373 if (ruleTemplate == null) {
374 RuleTemplateService ruleTemplateService = (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE);
375 ruleTemplate = ruleTemplateService.findByRuleTemplateName(getRouteMethodName());
376 }
377 return ruleTemplate;
378 }
379
380 public String getNodeType() {
381 return nodeType;
382 }
383
384 public void setNodeType(String nodeType) {
385 this.nodeType = nodeType;
386 }
387
388 public BranchPrototype getBranch() {
389 return branch;
390 }
391
392 public void setBranch(BranchPrototype branch) {
393 this.branch = branch;
394 }
395
396 //@PrePersist
397 public void beforeInsert(){
398 OrmUtils.populateAutoIncValue(this, KEWServiceLocator.getEntityManagerFactory().createEntityManager());
399 }
400
401 /**
402 * This overridden method ...
403 *
404 * @see java.lang.Object#toString()
405 */
406 @Override
407 public String toString() {
408 return "RouteNode[routeNodeName="+routeNodeName+", nodeType="+nodeType+", activationType="+activationType+"]";
409 }
410
411 @Override
412 public Long getVersionNumber() {
413 if (lockVerNbr == null) {
414 return null;
415 }
416 return Long.valueOf(lockVerNbr.longValue());
417 }
418
419 @Override
420 public String getId() {
421 if (routeNodeId == null) {
422 return null;
423 }
424 return routeNodeId.toString();
425 }
426
427 @Override
428 public String getName() {
429 return getRouteNodeName();
430 }
431
432 @Override
433 public boolean isFinalApproval() {
434 if (finalApprovalInd == null) {
435 return false;
436 }
437 return finalApprovalInd.booleanValue();
438 }
439
440 @Override
441 public boolean isMandatory() {
442 if (mandatoryRouteInd == null) {
443 return false;
444 }
445 return mandatoryRouteInd.booleanValue();
446 }
447
448 @Override
449 public String getExceptionGroupId() {
450 return exceptionWorkgroupId;
451 }
452
453 @Override
454 public String getType() {
455 return nodeType;
456 }
457
458 @Override
459 public String getBranchName() {
460 if (branch == null) {
461 return null;
462 }
463 return branch.getName();
464 }
465
466 @Override
467 public String getNextDocumentStatus() {
468 return nextDocStatus;
469 }
470
471 @Override
472 public List<? extends RouteNodeConfigurationParameterContract> getConfigurationParameters() {
473 return configParams;
474 }
475
476 @Override
477 public List<String> getPreviousNodeIds() {
478 List<String> previousNodeIds = new ArrayList<String>();
479 if (previousNodes != null) {
480 for (RouteNode previousNode : previousNodes) {
481 previousNodeIds.add(previousNode.getRouteNodeId().toString());
482 }
483 }
484 return previousNodeIds;
485 }
486
487 @Override
488 public List<String> getNextNodeIds() {
489 List<String> nextNodeIds = new ArrayList<String>();
490 if (nextNodeIds != null) {
491 for (RouteNode nextNode : nextNodes) {
492 nextNodeIds.add(nextNode.getRouteNodeId().toString());
493 }
494 }
495 return nextNodeIds;
496 }
497
498
499
500 }