001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.kew.engine.node;
017
018import java.io.Serializable;
019import java.util.ArrayList;
020import java.util.Iterator;
021import java.util.List;
022
023import javax.persistence.CascadeType;
024import javax.persistence.Column;
025import javax.persistence.Entity;
026import javax.persistence.FetchType;
027import javax.persistence.GeneratedValue;
028import javax.persistence.Id;
029import javax.persistence.JoinColumn;
030import javax.persistence.JoinTable;
031import javax.persistence.ManyToMany;
032import javax.persistence.ManyToOne;
033import javax.persistence.NamedQueries;
034import javax.persistence.NamedQuery;
035import javax.persistence.OneToMany;
036import javax.persistence.OneToOne;
037import javax.persistence.Table;
038import javax.persistence.Version;
039
040import org.apache.commons.lang.builder.ToStringBuilder;
041import org.hibernate.annotations.Fetch;
042import org.hibernate.annotations.FetchMode;
043import org.hibernate.annotations.GenericGenerator;
044import org.hibernate.annotations.Parameter;
045import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
046import org.kuali.rice.kew.api.document.node.RouteNodeInstanceState;
047import org.kuali.rice.kew.doctype.bo.DocumentType;
048import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
049import org.kuali.rice.kew.service.KEWServiceLocator;
050
051
052/**
053 * Represents a materialized instance of a {@link RouteNode} definition on a {@link DocumentRouteHeaderValue}.  Node instances
054 * are generated by the engine using the {@link RouteNode} as a prototype and connected as a 
055 * Directed Acyclic Graph.
056 *
057 * @author Kuali Rice Team (rice.collab@kuali.org)
058 */
059@Entity
060@Table(name="KREW_RTE_NODE_INSTN_T")
061//@Sequence(name="KREW_RTE_NODE_S",property="routeNodeInstanceId")
062@NamedQueries({
063        @NamedQuery(name="RouteNodeInstance.FindByRouteNodeInstanceId",query="select r from RouteNodeInstance r where r.routeNodeInstanceId = :routeNodeInstanceId"),
064        @NamedQuery(name="RouteNodeInstance.FindActiveNodeInstances",query="select r from RouteNodeInstance r where r.documentId = :documentId and r.active = true"),
065        @NamedQuery(name="RouteNodeInstance.FindTerminalNodeInstances",query="select r from RouteNodeInstance r where r.documentId = :documentId and r.active = false and r.complete = true"),
066        @NamedQuery(name="RouteNodeInstance.FindInitialNodeInstances",query="select d.initialRouteNodeInstances from DocumentRouteHeaderValue d where d.documentId = :documentId"),
067        @NamedQuery(name="RouteNodeInstance.FindProcessNodeInstances", query="select r from RouteNodeInstance r where r.process.routeNodeInstanceId = :processId"),
068        @NamedQuery(name="RouteNodeInstance.FindRouteNodeInstances", query="select r from RouteNodeInstance r where r.documentId = :documentId")
069})
070public class RouteNodeInstance implements Serializable {
071    
072        private static final long serialVersionUID = 7183670062805580420L;
073        
074        @Id
075        @GeneratedValue(generator="KREW_RTE_NODE_S")
076        @GenericGenerator(name="KREW_RTE_NODE_S",strategy="org.hibernate.id.enhanced.SequenceStyleGenerator",parameters={
077                        @Parameter(name="sequence_name",value="KREW_RTE_NODE_S"),
078                        @Parameter(name="value_column",value="id")
079        })
080        @Column(name="RTE_NODE_INSTN_ID")
081        private String routeNodeInstanceId;
082    @Column(name="DOC_HDR_ID")
083        private String documentId;
084    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
085        @JoinColumn(name="BRCH_ID")
086        private Branch branch;
087    @OneToOne(fetch=FetchType.EAGER)
088        @JoinColumn(name="RTE_NODE_ID")
089    private RouteNode routeNode;
090    @Column(name="ACTV_IND")
091    private boolean active = false;
092    @Column(name="CMPLT_IND")
093    private boolean complete = false;
094    @Column(name="INIT_IND")
095    private boolean initial = true;
096    @OneToOne(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.MERGE})
097        @JoinColumn(name="PROC_RTE_NODE_INSTN_ID")
098        private RouteNodeInstance process;
099    
100    @ManyToMany(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
101    @JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T", joinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID"), inverseJoinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID"))
102    @Fetch(value = FetchMode.SELECT)
103    private List<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>();
104    
105    @ManyToMany(fetch=FetchType.EAGER, mappedBy="nextNodeInstances")
106    @Fetch(value = FetchMode.SELECT)
107    //@JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T", joinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID"), inverseJoinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID"))
108    private List<RouteNodeInstance> previousNodeInstances = new ArrayList<RouteNodeInstance>();
109
110    @OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, mappedBy="nodeInstance", orphanRemoval=true)    
111    @Fetch(value = FetchMode.SELECT)
112    private List<NodeState> state = new ArrayList<NodeState>();
113        
114    @Version
115        @Column(name="VER_NBR")
116        private Integer lockVerNbr;
117    
118    private List<DocumentRouteHeaderValue> initialDocumentRouteHeaderValues = new ArrayList<DocumentRouteHeaderValue>();
119
120    public boolean isActive() {
121        return active;
122    }
123    public void setActive(boolean active) {
124        this.active = active;
125    }
126    
127    public boolean isComplete() {
128        return complete;
129    }
130    public void setComplete(boolean complete) {
131        this.complete = complete;
132    }
133    public Branch getBranch() {
134        return branch;
135    }
136    public void setBranch(Branch branch) {
137        this.branch = branch;
138    }
139    public RouteNode getRouteNode() {
140        return routeNode;
141    }
142    public void setRouteNode(RouteNode node) {
143        this.routeNode = node;
144    }
145    public String getRouteNodeInstanceId() {
146        return routeNodeInstanceId;
147    }
148    public void setRouteNodeInstanceId(String routeNodeInstanceId) {
149        this.routeNodeInstanceId = routeNodeInstanceId;
150    }
151    public String getDocumentId() {
152        return documentId;
153    }
154    public void setDocumentId(String documentId) {
155        this.documentId = documentId;
156    }
157    public List<RouteNodeInstance> getNextNodeInstances() {
158        return nextNodeInstances;
159    }
160    public RouteNodeInstance getNextNodeInstance(int index) {
161        while (getNextNodeInstances().size() <= index) {
162                nextNodeInstances.add(new RouteNodeInstance());
163        }
164        return (RouteNodeInstance) getNextNodeInstances().get(index);
165    }
166    public void setNextNodeInstances(List<RouteNodeInstance> nextNodeInstances) {
167        this.nextNodeInstances = nextNodeInstances;
168    }
169    public List<RouteNodeInstance> getPreviousNodeInstances() {
170        return previousNodeInstances;
171    }
172    public RouteNodeInstance getPreviousNodeInstance(int index) {
173        while (previousNodeInstances.size() <= index) {
174                previousNodeInstances.add(new RouteNodeInstance());
175        }
176        return (RouteNodeInstance) getPreviousNodeInstances().get(index);
177    }
178    public void setPreviousNodeInstances(List<RouteNodeInstance> previousNodeInstances) {
179        this.previousNodeInstances = previousNodeInstances;
180    }
181    public boolean isInitial() {
182        return initial;
183    }
184    public void setInitial(boolean initial) {
185        this.initial = initial;
186    }
187    public List<NodeState> getState() {
188        return state;
189    }
190    public void setState(List<NodeState> state) {
191        this.state.clear();
192        this.state.addAll(state);
193        //this.state = state;
194    }
195    public RouteNodeInstance getProcess() {
196                return process;
197        }
198        public void setProcess(RouteNodeInstance process) {
199                this.process = process;
200        }
201        public Integer getLockVerNbr() {
202        return lockVerNbr;
203    }
204    public void setLockVerNbr(Integer lockVerNbr) {
205        this.lockVerNbr = lockVerNbr;
206    }
207    
208    public NodeState getNodeState(String key) {
209        for (Iterator iter = getState().iterator(); iter.hasNext();) {
210            NodeState nodeState = (NodeState) iter.next();
211            if (nodeState.getKey().equals(key)) {
212                return nodeState;
213            }
214        }
215        return null;
216    }
217    
218    public void addNodeState(NodeState state) {
219        this.state.add(state);
220        state.setNodeInstance(this);
221    }
222    
223    public void removeNodeState(String key) {
224        for (Iterator iter = getState().iterator(); iter.hasNext();) {
225            NodeState nodeState = (NodeState) iter.next();
226            if (nodeState.getKey().equals(key)) {
227                iter.remove();
228                break;
229            }
230        }
231    }
232    
233    public void addNextNodeInstance(RouteNodeInstance nextNodeInstance) {
234        nextNodeInstances.add(nextNodeInstance);
235        nextNodeInstance.getPreviousNodeInstances().add(this);
236    }
237    
238    public void removeNextNodeInstance(RouteNodeInstance nextNodeInstance) {
239        nextNodeInstances.remove(nextNodeInstance);
240        nextNodeInstance.getPreviousNodeInstances().remove(this);
241    }
242    
243    public void clearNextNodeInstances() {
244        for (Iterator iterator = nextNodeInstances.iterator(); iterator.hasNext();) {
245            RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iterator.next();
246            iterator.remove();
247            nextNodeInstance.getPreviousNodeInstances().remove(this);
248        }
249    }
250    
251    public String getName() {
252        return (getRouteNode() == null ? null : getRouteNode().getRouteNodeName());
253    }
254    
255    public boolean isInProcess() {
256        return getProcess() != null;
257    }
258    
259    public DocumentType getDocumentType() {
260        return KEWServiceLocator.getDocumentTypeService().findByDocumentId(getDocumentId());
261    }
262    
263    /*
264     * methods used to display route node instances' data on documentoperation.jsp
265     */
266    
267    public NodeState getNodeStateByIndex(int index){
268        while (state.size() <= index) {
269            state.add(new NodeState());
270        }
271        return (NodeState) getState().get(index);
272    }   
273
274    public void populateState(List<NodeState> state) {
275        this.state.addAll(state);
276     }
277
278    public List<DocumentRouteHeaderValue> getInitialDocumentRouteHeaderValues() {
279        return initialDocumentRouteHeaderValues;
280    }
281
282    public void setInitialDocumentRouteHeaderValues(List<DocumentRouteHeaderValue> initialDocumentRouteHeaderValues) {
283        this.initialDocumentRouteHeaderValues = initialDocumentRouteHeaderValues;
284    }
285    
286    public String toString() {
287        return new ToStringBuilder(this)
288            .append("routeNodeInstanceId", routeNodeInstanceId)
289            .append("documentId", documentId)
290            .append("branch", branch == null ? null : branch.getBranchId())
291            .append("routeNode", routeNode == null ? null : routeNode.getRouteNodeName())
292            .append("active", active)
293            .append("complete", complete)
294            .append("initial", initial)
295            .append("process", process)
296            .append("state", state == null ? null : state.size())
297            .toString();
298    }
299    
300        //@PrePersist
301        public void beforeInsert(){
302                OrmUtils.populateAutoIncValue(this, KEWServiceLocator.getEntityManagerFactory().createEntityManager());
303        }
304
305
306        public static org.kuali.rice.kew.api.document.node.RouteNodeInstance to(RouteNodeInstance routeNodeInstance) {
307                if (routeNodeInstance == null) {
308                        return null;
309                }
310                org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder builder = org.kuali.rice.kew.api.document.node
311                .RouteNodeInstance.Builder.create();
312                builder.setActive(routeNodeInstance.isActive());
313                builder.setBranchId(routeNodeInstance.getBranch().getBranchId());
314                builder.setComplete(routeNodeInstance.isComplete());
315                builder.setDocumentId(routeNodeInstance.getDocumentId());
316                builder.setId(routeNodeInstance.getRouteNodeInstanceId());
317                builder.setInitial(routeNodeInstance.isInitial());
318                builder.setName(routeNodeInstance.getName());
319                if (routeNodeInstance.getProcess() != null) {
320                        builder.setProcessId(routeNodeInstance.getProcess().getRouteNodeInstanceId());
321                }
322                builder.setRouteNodeId(routeNodeInstance.getRouteNode().getRouteNodeId());
323                List<RouteNodeInstanceState.Builder> states = new ArrayList<RouteNodeInstanceState.Builder>();
324                for (NodeState stateBo : routeNodeInstance.getState()) {
325                        RouteNodeInstanceState.Builder stateBuilder = RouteNodeInstanceState.Builder.create();
326                        stateBuilder.setId(stateBo.getStateId());
327                        stateBuilder.setKey(stateBo.getKey());
328                        stateBuilder.setValue(stateBo.getValue());
329                        states.add(stateBuilder);
330                }
331                builder.setState(states);
332
333        List<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder> nextNodes = new ArrayList<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder>();
334        if (routeNodeInstance.getNextNodeInstances() != null) {
335            for (RouteNodeInstance next : routeNodeInstance.getNextNodeInstances()) {
336                // KULRICE-8152 - During load testing, sometimes routeNodeInstance.getNextNodeInstances() returns an
337                // arraylist with size = 1 but all elements are null, which causes a "contract was null" error when the
338                // create is called.  This check to see if next is not null prevents the error from occurring.
339                if (next != null) {
340                    // will this make things blow up?
341                    nextNodes.add(org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder.create(RouteNodeInstance.to(next)));
342                }
343            }
344        }
345        builder.setNextNodeInstances(nextNodes);
346
347                return builder.build();
348
349
350
351        }
352    
353}
354