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.builder.ToStringBuilder;
19  import org.kuali.rice.kew.api.document.node.RouteNodeInstanceState;
20  import org.kuali.rice.kew.doctype.bo.DocumentType;
21  import org.kuali.rice.kew.engine.node.dao.impl.RouteNodeDAOJpa;
22  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
23  import org.kuali.rice.kew.service.KEWServiceLocator;
24  import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
25  
26  import javax.persistence.CascadeType;
27  import javax.persistence.Column;
28  import javax.persistence.Entity;
29  import javax.persistence.GeneratedValue;
30  import javax.persistence.Id;
31  import javax.persistence.JoinColumn;
32  import javax.persistence.JoinTable;
33  import javax.persistence.ManyToMany;
34  import javax.persistence.ManyToOne;
35  import javax.persistence.NamedQueries;
36  import javax.persistence.NamedQuery;
37  import javax.persistence.OneToMany;
38  import javax.persistence.Table;
39  import javax.persistence.Version;
40  import java.io.Serializable;
41  import java.util.ArrayList;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  
46  /**
47   * Represents a materialized instance of a {@link RouteNode} definition on a {@link DocumentRouteHeaderValue}.  Node instances
48   * are generated by the engine using the {@link RouteNode} as a prototype and connected as a 
49   * Directed Acyclic Graph.
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  @Entity
54  @Table(name="KREW_RTE_NODE_INSTN_T")
55  @NamedQueries({
56  	@NamedQuery(name= RouteNodeDAOJpa.FIND_INITIAL_NODE_INSTANCES_NAME,
57              query=RouteNodeDAOJpa.FIND_INITIAL_NODE_INSTANCES_QUERY)
58  })
59  public class RouteNodeInstance implements Serializable {
60      
61  	private static final long serialVersionUID = 7183670062805580420L;
62  	
63  	@Id
64  	@GeneratedValue(generator="KREW_RTE_NODE_S")
65      @PortableSequenceGenerator(name="KREW_RTE_NODE_S")
66  	@Column(name="RTE_NODE_INSTN_ID")
67  	private String routeNodeInstanceId;
68  
69      @Column(name="DOC_HDR_ID")
70  	private String documentId;
71  
72      @ManyToOne(cascade = CascadeType.ALL)
73  	@JoinColumn(name="BRCH_ID")
74  	private Branch branch;
75  
76      @ManyToOne
77      @JoinColumn(name="RTE_NODE_ID", nullable = false)
78      private RouteNode routeNode;
79  
80      @Column(name="ACTV_IND")
81      private boolean active = false;
82  
83      @Column(name="CMPLT_IND")
84      private boolean complete = false;
85  
86      @Column(name="INIT_IND")
87      private boolean initial = true;
88  
89      @ManyToOne(cascade = CascadeType.ALL)
90  	@JoinColumn(name="PROC_RTE_NODE_INSTN_ID")
91  	private RouteNodeInstance process;
92  
93      @ManyToMany(cascade = CascadeType.ALL)
94      @JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T",
95              joinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID"),
96              inverseJoinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID"))
97      private List<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>();
98      
99      @ManyToMany(mappedBy = "nextNodeInstances")
100     @JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T",
101             joinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID", updatable = false, insertable = false),
102             inverseJoinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID", updatable = false, insertable = false))
103     private List<RouteNodeInstance> previousNodeInstances = new ArrayList<RouteNodeInstance>();
104 
105     @OneToMany(cascade = CascadeType.ALL, mappedBy = "nodeInstance", orphanRemoval = true)
106     private List<NodeState> state = new ArrayList<NodeState>();
107     	
108     @Version
109 	@Column(name="VER_NBR")
110 	private Integer lockVerNbr;
111 
112     public boolean isActive() {
113         return active;
114     }
115     public void setActive(boolean active) {
116         this.active = active;
117     }
118     
119     public boolean isComplete() {
120         return complete;
121     }
122     public void setComplete(boolean complete) {
123         this.complete = complete;
124     }
125     public Branch getBranch() {
126         return branch;
127     }
128     public void setBranch(Branch branch) {
129         this.branch = branch;
130     }
131     public RouteNode getRouteNode() {
132         return routeNode;
133     }
134     public void setRouteNode(RouteNode node) {
135         this.routeNode = node;
136     }
137     public String getRouteNodeInstanceId() {
138         return routeNodeInstanceId;
139     }
140     public void setRouteNodeInstanceId(String routeNodeInstanceId) {
141         this.routeNodeInstanceId = routeNodeInstanceId;
142     }
143     public String getDocumentId() {
144         return documentId;
145     }
146     public void setDocumentId(String documentId) {
147         this.documentId = documentId;
148     }
149     public List<RouteNodeInstance> getNextNodeInstances() {
150         return nextNodeInstances;
151     }
152     public RouteNodeInstance getNextNodeInstance(int index) {
153     	while (getNextNodeInstances().size() <= index) {
154     		nextNodeInstances.add(new RouteNodeInstance());
155     	}
156     	return getNextNodeInstances().get(index);
157     }
158     public void setNextNodeInstances(List<RouteNodeInstance> nextNodeInstances) {
159         this.nextNodeInstances = nextNodeInstances;
160     }
161     public List<RouteNodeInstance> getPreviousNodeInstances() {
162         return previousNodeInstances;
163     }
164     public RouteNodeInstance getPreviousNodeInstance(int index) {
165     	while (previousNodeInstances.size() <= index) {
166     		previousNodeInstances.add(new RouteNodeInstance());
167     	}
168     	return (RouteNodeInstance) getPreviousNodeInstances().get(index);
169     }
170     public void setPreviousNodeInstances(List<RouteNodeInstance> previousNodeInstances) {
171         this.previousNodeInstances = previousNodeInstances;
172     }
173     public boolean isInitial() {
174         return initial;
175     }
176     public void setInitial(boolean initial) {
177         this.initial = initial;
178     }
179     public List<NodeState> getState() {
180         return state;
181     }
182     public void setState(List<NodeState> state) {
183         this.state.clear();
184     	this.state.addAll(state);
185         //this.state = state;
186     }
187     public RouteNodeInstance getProcess() {
188 		return process;
189 	}
190 	public void setProcess(RouteNodeInstance process) {
191 		this.process = process;
192 	}
193 	public Integer getLockVerNbr() {
194         return lockVerNbr;
195     }
196     public void setLockVerNbr(Integer lockVerNbr) {
197         this.lockVerNbr = lockVerNbr;
198     }
199 
200     public String getRouteNodeId() {
201         if (getRouteNode() == null) {
202             return null;
203         }
204         return getRouteNode().getRouteNodeId();
205     }
206 
207     public NodeState getNodeState(String key) {
208         for (Iterator iter = getState().iterator(); iter.hasNext();) {
209             NodeState nodeState = (NodeState) iter.next();
210             if (nodeState.getKey().equals(key)) {
211                 return nodeState;
212             }
213         }
214         return null;
215     }
216     
217     public void addNodeState(NodeState state) {
218         this.state.add(state);
219         state.setNodeInstance(this);
220     }
221     
222     public void removeNodeState(String key) {
223         for (Iterator iter = getState().iterator(); iter.hasNext();) {
224             NodeState nodeState = (NodeState) iter.next();
225             if (nodeState.getKey().equals(key)) {
226                 iter.remove();
227                 break;
228             }
229         }
230     }
231     
232     public void addNextNodeInstance(RouteNodeInstance nextNodeInstance) {
233         nextNodeInstances.add(nextNodeInstance);
234         nextNodeInstance.getPreviousNodeInstances().add(this);
235     }
236     
237     public void removeNextNodeInstance(RouteNodeInstance nextNodeInstance) {
238         nextNodeInstances.remove(nextNodeInstance);
239         nextNodeInstance.getPreviousNodeInstances().remove(this);
240     }
241     
242     public void clearNextNodeInstances() {
243         for (Iterator iterator = nextNodeInstances.iterator(); iterator.hasNext();) {
244             RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iterator.next();
245             iterator.remove();
246             nextNodeInstance.getPreviousNodeInstances().remove(this);
247         }
248     }
249     
250     public String getName() {
251         return (getRouteNode() == null ? null : getRouteNode().getRouteNodeName());
252     }
253     
254     public boolean isInProcess() {
255         return getProcess() != null;
256     }
257     
258     public DocumentType getDocumentType() {
259         return KEWServiceLocator.getDocumentTypeService().findByDocumentId(getDocumentId());
260     }
261     
262     /*
263      * methods used to display route node instances' data on documentoperation.jsp
264      */
265     
266     public NodeState getNodeStateByIndex(int index){
267     	while (state.size() <= index) {
268             state.add(new NodeState());
269         }
270         return getState().get(index);
271     }   
272 
273     public void populateState(List<NodeState> state) {
274         this.state.addAll(state);
275     }
276 
277     public RouteNodeInstance deepCopy(Map<Object, Object> visited) {
278         if (visited.containsKey(this)) {
279             return (RouteNodeInstance)visited.get(this);
280         }
281         RouteNodeInstance copy = new RouteNodeInstance();
282         visited.put(this, copy);
283         copy.routeNodeInstanceId = routeNodeInstanceId;
284         copy.documentId = documentId;
285         copy.active = active;
286         copy.complete = complete;
287         copy.initial = initial;
288         copy.lockVerNbr = lockVerNbr;
289         // no need to deep copy route node because it's static configuration
290         copy.routeNode = routeNode;
291         if (branch != null) {
292             copy.branch = branch.deepCopy(visited);
293         }
294         if (process != null) {
295             copy.process = process.deepCopy(visited);
296         }
297         if (nextNodeInstances != null) {
298             List<RouteNodeInstance> copies = new ArrayList<RouteNodeInstance>();
299             for (RouteNodeInstance nextNodeInstance : nextNodeInstances) {
300                 copies.add(nextNodeInstance.deepCopy(visited));
301             }
302             copy.nextNodeInstances = copies;
303         }
304         if (previousNodeInstances != null) {
305             List<RouteNodeInstance> copies = new ArrayList<RouteNodeInstance>();
306             for (RouteNodeInstance previousNodeInstance : previousNodeInstances) {
307                 copies.add(previousNodeInstance.deepCopy(visited));
308             }
309             copy.previousNodeInstances = copies;
310         }
311         if (state != null) {
312             List<NodeState> copies = new ArrayList<NodeState>();
313             for (NodeState aState : state) {
314                 copies.add(aState.deepCopy(visited));
315             }
316             copy.state = copies;
317         }
318         return copy;
319     }
320     
321     public String toString() {
322         return new ToStringBuilder(this)
323             .append("routeNodeInstanceId", routeNodeInstanceId)
324             .append("documentId", documentId)
325             .append("branch", branch == null ? null : branch.getBranchId())
326             .append("routeNode", routeNode == null ? null : routeNode.getRouteNodeName() + " " + routeNode.getRouteNodeId())
327             .append("active", active)
328             .append("complete", complete)
329             .append("initial", initial)
330             .append("process", process)
331             .append("state", state == null ? null : state.size())
332             .toString();
333     }
334 
335 	public static org.kuali.rice.kew.api.document.node.RouteNodeInstance to(RouteNodeInstance routeNodeInstance) {
336 		if (routeNodeInstance == null) {
337 			return null;
338 		}
339 		org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder builder = org.kuali.rice.kew.api.document.node
340                 .RouteNodeInstance.Builder.create();
341 		builder.setActive(routeNodeInstance.isActive());
342 		builder.setBranchId(routeNodeInstance.getBranch().getBranchId());
343 		builder.setComplete(routeNodeInstance.isComplete());
344 		builder.setDocumentId(routeNodeInstance.getDocumentId());
345 		builder.setId(routeNodeInstance.getRouteNodeInstanceId());
346 		builder.setInitial(routeNodeInstance.isInitial());
347 		builder.setName(routeNodeInstance.getName());
348 		if (routeNodeInstance.getProcess() != null) {
349 			builder.setProcessId(routeNodeInstance.getProcess().getRouteNodeInstanceId());
350 		}
351 		builder.setRouteNodeId(routeNodeInstance.getRouteNode().getRouteNodeId());
352 		List<RouteNodeInstanceState.Builder> states = new ArrayList<RouteNodeInstanceState.Builder>();
353 		for (NodeState stateBo : routeNodeInstance.getState()) {
354 			RouteNodeInstanceState.Builder stateBuilder = RouteNodeInstanceState.Builder.create();
355 			stateBuilder.setId(stateBo.getStateId());
356 			stateBuilder.setKey(stateBo.getKey());
357 			stateBuilder.setValue(stateBo.getValue());
358 			states.add(stateBuilder);
359 		}
360 		builder.setState(states);
361 
362         List<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder> nextNodes = new ArrayList<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder>();
363         if (routeNodeInstance.getNextNodeInstances() != null) {
364             for (RouteNodeInstance next : routeNodeInstance.getNextNodeInstances()) {
365                 // KULRICE-8152 - During load testing, sometimes routeNodeInstance.getNextNodeInstances() returns an
366                 // arraylist with size = 1 but all elements are null, which causes a "contract was null" error when the
367                 // create is called.  This check to see if next is not null prevents the error from occurring.
368                 if (next != null) {
369                     // will this make things blow up?
370                     nextNodes.add(org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder.create(RouteNodeInstance.to(next)));
371                 }
372             }
373         }
374         builder.setNextNodeInstances(nextNodes);
375 
376 		return builder.build();
377 
378 
379 
380 	}
381     
382 }
383