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.dao.impl;
017
018import org.kuali.rice.core.api.criteria.QueryByCriteria;
019import org.kuali.rice.kew.api.KEWPropertyConstants;
020import org.kuali.rice.kew.engine.node.NodeState;
021import org.kuali.rice.kew.engine.node.RouteNode;
022import org.kuali.rice.kew.engine.node.RouteNodeInstance;
023import org.kuali.rice.kew.engine.node.dao.RouteNodeDAO;
024import org.kuali.rice.kew.service.KEWServiceLocator;
025import org.kuali.rice.krad.data.DataObjectService;
026import org.springframework.beans.factory.annotation.Required;
027import org.springframework.dao.DataAccessException;
028import org.springframework.jdbc.core.JdbcTemplate;
029import org.springframework.jdbc.core.PreparedStatementCallback;
030import org.springframework.jdbc.core.PreparedStatementCreator;
031
032import javax.persistence.EntityManager;
033import javax.persistence.Query;
034import javax.sql.DataSource;
035import java.sql.Connection;
036import java.sql.PreparedStatement;
037import java.sql.ResultSet;
038import java.sql.SQLException;
039import java.util.ArrayList;
040import java.util.Iterator;
041import java.util.List;
042
043import static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
044
045public class RouteNodeDAOJpa implements RouteNodeDAO {
046
047        private EntityManager entityManager;
048    private DataObjectService dataObjectService;
049
050    public static final String FIND_INITIAL_NODE_INSTANCES_NAME = "RouteNodeInstance.FindInitialNodeInstances";
051    public static final String FIND_INITIAL_NODE_INSTANCES_QUERY = "select d.initialRouteNodeInstances from "
052            + "DocumentRouteHeaderValue d where d.documentId = :documentId";
053
054    /**
055         * @return the entityManager
056         */
057        public EntityManager getEntityManager() {
058                return this.entityManager;
059        }
060
061        /**
062         * @param entityManager the entityManager to set
063         */
064        public void setEntityManager(EntityManager entityManager) {
065                this.entityManager = entityManager;
066        }
067
068    public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId) {
069        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
070                equal(KEWPropertyConstants.ROUTE_NODE_INSTANCE_ID,nodeInstanceId)
071        );
072
073        List<RouteNodeInstance> routeNodeInstances = getDataObjectService().findMatching(
074                RouteNodeInstance.class,queryByCriteria.build()).getResults();
075        if(routeNodeInstances != null && routeNodeInstances.size() > 0){
076            return routeNodeInstances.get(0);
077        }
078        return null;
079    }
080
081    @SuppressWarnings("unchecked")
082    public List<RouteNodeInstance> getActiveNodeInstances(String documentId) {
083        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
084                equal(KEWPropertyConstants.DOCUMENT_ID,documentId),
085                equal(KEWPropertyConstants.ACTIVE,true)
086        );
087        return getDataObjectService().findMatching(RouteNodeInstance.class,
088                    queryByCriteria.build()).getResults();
089    }
090
091    private static final String CURRENT_ROUTE_NODE_NAMES_SQL = "SELECT rn.nm" +
092                " FROM krew_rte_node_t rn," +
093                "      krew_rte_node_instn_t rni" +
094                " LEFT JOIN krew_rte_node_instn_lnk_t rnl" +
095                "   ON rnl.from_rte_node_instn_id = rni.rte_node_instn_id" +
096                " WHERE rn.rte_node_id = rni.rte_node_id AND" +
097                "       rni.doc_hdr_id = ? AND" +
098                "       rnl.from_rte_node_instn_id IS NULL";
099
100        @Override
101        public List<String> getCurrentRouteNodeNames(final String documentId) {
102            final DataSource dataSource = KEWServiceLocator.getDataSource();
103            JdbcTemplate template = new JdbcTemplate(dataSource);
104            List<String> names = template.execute(new PreparedStatementCreator() {
105                        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
106                            return connection.prepareStatement(CURRENT_ROUTE_NODE_NAMES_SQL);
107                        }
108                    }, new PreparedStatementCallback<List<String>>() {
109                        public List<String> doInPreparedStatement(
110                                PreparedStatement statement) throws SQLException, DataAccessException {
111                            List<String> routeNodeNames = new ArrayList<String>();
112                            statement.setString(1, documentId);
113                            ResultSet rs = statement.executeQuery();
114                            try {
115                                while (rs.next()) {
116                                    String name = rs.getString("nm");
117                                    routeNodeNames.add(name);
118                                }
119                            } finally {
120                                if (rs != null) {
121                                    rs.close();
122                                }
123                            }
124                            return routeNodeNames;
125                        }
126                    }
127            );
128            return names;
129        }
130    
131    @Override
132        public List<String> getActiveRouteNodeNames(final String documentId) {
133        final DataSource dataSource = KEWServiceLocator.getDataSource();
134        JdbcTemplate template = new JdbcTemplate(dataSource);
135        List<String> names = template.execute(
136                                new PreparedStatementCreator() {
137                                        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
138                                                PreparedStatement statement = connection.prepareStatement(
139                                                                "SELECT rn.nm FROM krew_rte_node_t rn, krew_rte_node_instn_t rni WHERE rn.rte_node_id "
140                                        + "= rni.rte_node_id AND rni.doc_hdr_id = ? AND rni.actv_ind = ?");
141                                                return statement;
142                                        }
143                                },
144                                new PreparedStatementCallback<List<String>>() {
145                                        public List<String> doInPreparedStatement(PreparedStatement statement) throws SQLException, DataAccessException {
146                                                List<String> routeNodeNames = new ArrayList<String>();
147                                                statement.setString(1, documentId);
148                                                statement.setBoolean(2, Boolean.TRUE);
149                                                ResultSet rs = statement.executeQuery();
150                                                try {
151                                                        while(rs.next()) {
152                                                                String name = rs.getString("nm");
153                                                                routeNodeNames.add(name);
154                                                        }
155                                                } finally {
156                                                        if(rs != null) {
157                                                                rs.close();
158                                                        }
159                                                }
160                                                return routeNodeNames;
161                                        }
162                                });
163        return names;
164        }
165
166    @SuppressWarnings("unchecked")
167    public List<RouteNodeInstance> getTerminalNodeInstances(String documentId) {
168        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
169                equal(KEWPropertyConstants.DOCUMENT_ID,documentId),
170                equal(KEWPropertyConstants.ACTIVE,false),
171                equal(KEWPropertyConstants.COMPLETE,true)
172        );
173
174        //FIXME: Can we do this better using just the JPQL query?
175        List<RouteNodeInstance> terminalNodes = new ArrayList<RouteNodeInstance>();
176        List<RouteNodeInstance> routeNodeInstances = getDataObjectService().
177                findMatching(RouteNodeInstance.class,queryByCriteria.build()).getResults();
178                for (RouteNodeInstance routeNodeInstance : routeNodeInstances) {
179                    if (routeNodeInstance.getNextNodeInstances().isEmpty()) {
180                        terminalNodes.add(routeNodeInstance);
181                    }
182                }
183                return terminalNodes;
184    }
185
186    @Override
187    public List<String> getTerminalRouteNodeNames(final String documentId) {
188        final DataSource dataSource = KEWServiceLocator.getDataSource();
189        JdbcTemplate template = new JdbcTemplate(dataSource);
190        List<String> names = template.execute(new PreparedStatementCreator() {
191                    public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
192                        PreparedStatement statement = connection.prepareStatement("SELECT rn.nm" +
193                                "  FROM krew_rte_node_t rn," +
194                                "       krew_rte_node_instn_t rni" +
195                                "  LEFT JOIN krew_rte_node_instn_lnk_t rnl" +
196                                "    ON rnl.from_rte_node_instn_id = rni.rte_node_instn_id" +
197                                "  WHERE rn.rte_node_id = rni.rte_node_id AND" +
198                                "        rni.doc_hdr_id = ? AND" +
199                                "        rni.actv_ind = ? AND" +
200                                "        rni.cmplt_ind = ? AND" +
201                                "        rnl.from_rte_node_instn_id IS NULL");
202                        return statement;
203                    }
204                }, new PreparedStatementCallback<List<String>>() {
205                    public List<String> doInPreparedStatement(
206                            PreparedStatement statement) throws SQLException, DataAccessException {
207                        List<String> routeNodeNames = new ArrayList<String>();
208                        statement.setString(1, documentId);
209                        statement.setBoolean(2, Boolean.FALSE);
210                        statement.setBoolean(3, Boolean.TRUE);
211                        ResultSet rs = statement.executeQuery();
212                        try {
213                            while (rs.next()) {
214                                String name = rs.getString("nm");
215                                routeNodeNames.add(name);
216                            }
217                        } finally {
218                            if (rs != null) {
219                                rs.close();
220                            }
221                        }
222                        return routeNodeNames;
223                    }
224                }
225        );
226        return names;
227    }
228
229    public List getInitialNodeInstances(String documentId) {
230        //FIXME: Not sure this query is returning what it needs to                                                    
231        Query query = entityManager.createNamedQuery(FIND_INITIAL_NODE_INSTANCES_NAME);
232        query.setParameter(KEWPropertyConstants.DOCUMENT_ID, documentId);
233                return (List)query.getResultList();
234    }
235
236    public NodeState findNodeState(Long nodeInstanceId, String key) {
237        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
238                equal(KEWPropertyConstants.ROUTE_NODE_INSTANCE_ID,nodeInstanceId.toString()),
239                equal(KEWPropertyConstants.KEY,key)
240        );
241
242        List<NodeState> nodeStates = getDataObjectService().findMatching(
243                        NodeState.class,queryByCriteria.build()).getResults();
244        if(nodeStates != null && nodeStates.size() > 0){
245            return nodeStates.get(0);
246        }
247        return null;
248    }
249
250    public RouteNode findRouteNodeByName(String documentTypeId, String name) {
251        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
252                equal(KEWPropertyConstants.DOCUMENT_TYPE_ID,documentTypeId),
253                equal(KEWPropertyConstants.ROUTE_NODE_NAME,name)
254        );
255        List<RouteNode> routeNodes = getDataObjectService().findMatching(
256                        RouteNode.class,queryByCriteria.build()).getResults();
257        if(routeNodes != null && routeNodes.size() > 0){
258            return routeNodes.get(0);
259        }
260        return null;
261    }
262
263    public List<RouteNode> findFinalApprovalRouteNodes(String documentTypeId) {
264        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
265                equal(KEWPropertyConstants.DOCUMENT_TYPE_ID,documentTypeId),
266                equal(KEWPropertyConstants.FINAL_APPROVAL,Boolean.TRUE)
267        );
268        return getDataObjectService().findMatching(RouteNode.class,queryByCriteria.build()).getResults();
269    }
270
271    public List findProcessNodeInstances(RouteNodeInstance process) {
272        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
273                equal(KEWPropertyConstants.PROCESS_ID,process.getRouteNodeInstanceId())
274        );
275        return getDataObjectService().findMatching(RouteNodeInstance.class,queryByCriteria.build()).getResults();
276    }
277
278    public List findRouteNodeInstances(String documentId) {
279        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
280                equal(KEWPropertyConstants.DOCUMENT_ID,documentId)
281        );
282        return getDataObjectService().findMatching(RouteNodeInstance.class,queryByCriteria.build()).getResults();
283    }
284
285    public void deleteLinksToPreNodeInstances(RouteNodeInstance routeNodeInstance) {
286                List<RouteNodeInstance> preNodeInstances = routeNodeInstance.getPreviousNodeInstances();
287                for (Iterator<RouteNodeInstance> preNodeInstanceIter = preNodeInstances.iterator(); preNodeInstanceIter.hasNext();) {
288                    RouteNodeInstance preNodeInstance = (RouteNodeInstance) preNodeInstanceIter.next();
289                    List<RouteNodeInstance> nextInstances = preNodeInstance.getNextNodeInstances();
290                    nextInstances.remove(routeNodeInstance);
291                    getEntityManager().merge(preNodeInstance);
292                }
293    }
294
295    public void deleteRouteNodeInstancesHereAfter(RouteNodeInstance routeNodeInstance) {
296        RouteNodeInstance rnInstance = findRouteNodeInstanceById(routeNodeInstance.getRouteNodeInstanceId());
297        entityManager.remove(rnInstance);
298    }
299
300    public void deleteNodeStateById(Long nodeStateId) {
301        QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates(
302                equal(KEWPropertyConstants.ROUTE_NODE_STATE_ID,nodeStateId)
303        );
304        List<NodeState> nodeStates = getDataObjectService().findMatching(
305                                NodeState.class,queryByCriteria.build()).getResults();
306        NodeState nodeState = null;
307        if(nodeStates != null && nodeStates.size() > 0){
308            nodeState = nodeStates.get(0);
309        }
310        getDataObjectService().delete(nodeState);
311    }
312
313    public void deleteNodeStates(List statesToBeDeleted) {
314                for (Iterator stateToBeDeletedIter = statesToBeDeleted.iterator(); stateToBeDeletedIter.hasNext();) {
315                    Long stateId = (Long) stateToBeDeletedIter.next();
316                    deleteNodeStateById(stateId);
317                }
318    }
319
320
321    public DataObjectService getDataObjectService() {
322        return dataObjectService;
323    }
324
325    @Required
326    public void setDataObjectService(DataObjectService dataObjectService) {
327        this.dataObjectService = dataObjectService;
328    }
329
330
331}