001/** 002 * Copyright 2005-2017 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.doctype.bo.DocumentType; 021import org.kuali.rice.kew.engine.RouteHelper; 022import org.kuali.rice.kew.engine.node.NodeState; 023import org.kuali.rice.kew.engine.node.RouteNode; 024import org.kuali.rice.kew.engine.node.RouteNodeInstance; 025import org.kuali.rice.kew.engine.node.dao.RouteNodeDAO; 026import org.kuali.rice.kew.service.KEWServiceLocator; 027import org.kuali.rice.krad.data.DataObjectService; 028import org.springframework.beans.factory.annotation.Required; 029import org.springframework.dao.DataAccessException; 030import org.springframework.jdbc.core.JdbcTemplate; 031import org.springframework.jdbc.core.PreparedStatementCallback; 032import org.springframework.jdbc.core.PreparedStatementCreator; 033 034import javax.persistence.EntityManager; 035import javax.persistence.Query; 036import javax.sql.DataSource; 037import java.sql.Connection; 038import java.sql.PreparedStatement; 039import java.sql.ResultSet; 040import java.sql.SQLException; 041import java.util.ArrayList; 042import java.util.Iterator; 043import java.util.List; 044 045import static org.kuali.rice.core.api.criteria.PredicateFactory.equal; 046 047public class RouteNodeDAOJpa implements RouteNodeDAO { 048 049 private EntityManager entityManager; 050 private DataObjectService dataObjectService; 051 052 public static final String FIND_INITIAL_NODE_INSTANCES_NAME = "RouteNodeInstance.FindInitialNodeInstances"; 053 public static final String FIND_INITIAL_NODE_INSTANCES_QUERY = "select d.initialRouteNodeInstances from " 054 + "DocumentRouteHeaderValue d where d.documentId = :documentId"; 055 056 /** 057 * @return the entityManager 058 */ 059 public EntityManager getEntityManager() { 060 return this.entityManager; 061 } 062 063 /** 064 * @param entityManager the entityManager to set 065 */ 066 public void setEntityManager(EntityManager entityManager) { 067 this.entityManager = entityManager; 068 } 069 070 public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId) { 071 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 072 equal(KEWPropertyConstants.ROUTE_NODE_INSTANCE_ID, nodeInstanceId) 073 ); 074 075 List<RouteNodeInstance> routeNodeInstances = getDataObjectService().findMatching( 076 RouteNodeInstance.class, queryByCriteria.build()).getResults(); 077 if (routeNodeInstances != null && routeNodeInstances.size() > 0) { 078 return routeNodeInstances.get(0); 079 } 080 return null; 081 } 082 083 @SuppressWarnings("unchecked") 084 public List<RouteNodeInstance> getActiveNodeInstances(String documentId) { 085 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 086 equal(KEWPropertyConstants.DOCUMENT_ID, documentId), 087 equal(KEWPropertyConstants.ACTIVE, true) 088 ); 089 return getDataObjectService().findMatching(RouteNodeInstance.class, 090 queryByCriteria.build()).getResults(); 091 } 092 093 private static final String CURRENT_ROUTE_NODE_NAMES_SQL = "SELECT rn.nm, rn.typ" + 094 " FROM krew_rte_node_t rn," + 095 " krew_rte_node_instn_t rni" + 096 " LEFT JOIN krew_rte_node_instn_lnk_t rnl" + 097 " ON rnl.from_rte_node_instn_id = rni.rte_node_instn_id" + 098 " WHERE rn.rte_node_id = rni.rte_node_id AND" + 099 " rni.doc_hdr_id = ? AND" + 100 " rnl.from_rte_node_instn_id IS NULL"; 101 102 @Override 103 public List<String> getCurrentRouteNodeNames(final String documentId) { 104 final DataSource dataSource = KEWServiceLocator.getDataSource(); 105 JdbcTemplate template = new JdbcTemplate(dataSource); 106 List<String> names = template.execute(new PreparedStatementCreator() { 107 public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 108 return connection.prepareStatement(CURRENT_ROUTE_NODE_NAMES_SQL); 109 } 110 }, new PreparedStatementCallback<List<String>>() { 111 public List<String> doInPreparedStatement( 112 PreparedStatement statement) throws SQLException, DataAccessException { 113 List<String> routeNodeNames = new ArrayList<String>(); 114 statement.setString(1, documentId); 115 ResultSet rs = statement.executeQuery(); 116 try { 117 while (rs.next()) { 118 String name = rs.getString("nm"); 119 routeNodeNames.add(name); 120 } 121 } finally { 122 if (rs != null) { 123 rs.close(); 124 } 125 } 126 return routeNodeNames; 127 } 128 } 129 ); 130 return names; 131 } 132 133 @Override 134 public List<String> getCurrentSimpleRouteNodeNames(final String documentId) { 135 final DataSource dataSource = KEWServiceLocator.getDataSource(); 136 JdbcTemplate template = new JdbcTemplate(dataSource); 137 List<String> names = template.execute(new PreparedStatementCreator() { 138 public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 139 return connection.prepareStatement(CURRENT_ROUTE_NODE_NAMES_SQL); 140 } 141 }, new PreparedStatementCallback<List<String>>() { 142 public List<String> doInPreparedStatement( 143 PreparedStatement statement) throws SQLException, DataAccessException { 144 List<String> routeNodeNames = new ArrayList<String>(); 145 statement.setString(1, documentId); 146 ResultSet rs = statement.executeQuery(); 147 try { 148 while (rs.next()) { 149 String name = rs.getString("nm"); 150 String type = rs.getString("typ"); 151 if (isSimpleNode(type)) { 152 routeNodeNames.add(name); 153 } 154 } 155 } finally { 156 if (rs != null) { 157 rs.close(); 158 } 159 } 160 return routeNodeNames; 161 } 162 } 163 ); 164 return names; 165 } 166 167 168 @Override 169 public List<String> getActiveRouteNodeNames(final String documentId) { 170 final DataSource dataSource = KEWServiceLocator.getDataSource(); 171 JdbcTemplate template = new JdbcTemplate(dataSource); 172 List<String> names = template.execute( 173 new PreparedStatementCreator() { 174 public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 175 PreparedStatement statement = connection.prepareStatement( 176 "SELECT rn.nm FROM krew_rte_node_t rn, krew_rte_node_instn_t rni WHERE rn.rte_node_id " 177 + "= rni.rte_node_id AND rni.doc_hdr_id = ? AND rni.actv_ind = ?"); 178 return statement; 179 } 180 }, 181 new PreparedStatementCallback<List<String>>() { 182 public List<String> doInPreparedStatement(PreparedStatement statement) throws SQLException, DataAccessException { 183 List<String> routeNodeNames = new ArrayList<String>(); 184 statement.setString(1, documentId); 185 statement.setBoolean(2, Boolean.TRUE); 186 ResultSet rs = statement.executeQuery(); 187 try { 188 while (rs.next()) { 189 String name = rs.getString("nm"); 190 routeNodeNames.add(name); 191 } 192 } finally { 193 if (rs != null) { 194 rs.close(); 195 } 196 } 197 return routeNodeNames; 198 } 199 }); 200 return names; 201 } 202 203 @Override 204 public List<String> getActiveSimpleRouteNodeNames(final String documentId) { 205 final DataSource dataSource = KEWServiceLocator.getDataSource(); 206 JdbcTemplate template = new JdbcTemplate(dataSource); 207 List<String> names = template.execute( 208 new PreparedStatementCreator() { 209 public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 210 PreparedStatement statement = connection.prepareStatement( 211 "SELECT rn.nm, rn.typ FROM krew_rte_node_t rn, krew_rte_node_instn_t rni WHERE rn.rte_node_id " 212 + "= rni.rte_node_id AND rni.doc_hdr_id = ? AND rni.actv_ind = ?"); 213 return statement; 214 } 215 }, 216 new PreparedStatementCallback<List<String>>() { 217 public List<String> doInPreparedStatement(PreparedStatement statement) throws SQLException, DataAccessException { 218 List<String> routeNodeNames = new ArrayList<String>(); 219 statement.setString(1, documentId); 220 statement.setBoolean(2, Boolean.TRUE); 221 ResultSet rs = statement.executeQuery(); 222 try { 223 while (rs.next()) { 224 String name = rs.getString("nm"); 225 String type = rs.getString("typ"); 226 if (isSimpleNode(type)) { 227 routeNodeNames.add(name); 228 } 229 } 230 } finally { 231 if (rs != null) { 232 rs.close(); 233 } 234 } 235 return routeNodeNames; 236 } 237 }); 238 return names; 239 } 240 241 /** 242 * This method is a bit of a hack because in order to check if a node is "simple" or not, we actually 243 * need to instantiate it's type and check the java class type. RouteHelper can do this for us but it 244 * needs an actual RouteNode object to do this work. To avoid the full cost of loading the full 245 * RouteNode object and it's associated DocumentType, we will mock out what we need here to get the job 246 * done. 247 */ 248 private boolean isSimpleNode(String type) { 249 // construct a "mock" RouteNode so that we can fool the RouteHelper 250 RouteNode routeNode = new RouteNode(); 251 routeNode.setNodeType(type); 252 // document type must be non-null since route helper will attempt to get the application id off of it 253 // though it's ok if the application id is null 254 routeNode.setDocumentType(new DocumentType()); 255 return new RouteHelper().isSimpleNode(routeNode); 256 } 257 258 @SuppressWarnings("unchecked") 259 public List<RouteNodeInstance> getTerminalNodeInstances(String documentId) { 260 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 261 equal(KEWPropertyConstants.DOCUMENT_ID, documentId), 262 equal(KEWPropertyConstants.ACTIVE, false), 263 equal(KEWPropertyConstants.COMPLETE, true) 264 ); 265 266 //FIXME: Can we do this better using just the JPQL query? 267 List<RouteNodeInstance> terminalNodes = new ArrayList<RouteNodeInstance>(); 268 List<RouteNodeInstance> routeNodeInstances = getDataObjectService(). 269 findMatching(RouteNodeInstance.class, queryByCriteria.build()).getResults(); 270 for (RouteNodeInstance routeNodeInstance : routeNodeInstances) { 271 if (routeNodeInstance.getNextNodeInstances().isEmpty()) { 272 terminalNodes.add(routeNodeInstance); 273 } 274 } 275 return terminalNodes; 276 } 277 278 @Override 279 public List<String> getTerminalRouteNodeNames(final String documentId) { 280 final DataSource dataSource = KEWServiceLocator.getDataSource(); 281 JdbcTemplate template = new JdbcTemplate(dataSource); 282 List<String> names = template.execute(new PreparedStatementCreator() { 283 public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 284 PreparedStatement statement = connection.prepareStatement("SELECT rn.nm" + 285 " FROM krew_rte_node_t rn," + 286 " krew_rte_node_instn_t rni" + 287 " LEFT JOIN krew_rte_node_instn_lnk_t rnl" + 288 " ON rnl.from_rte_node_instn_id = rni.rte_node_instn_id" + 289 " WHERE rn.rte_node_id = rni.rte_node_id AND" + 290 " rni.doc_hdr_id = ? AND" + 291 " rni.actv_ind = ? AND" + 292 " rni.cmplt_ind = ? AND" + 293 " rnl.from_rte_node_instn_id IS NULL"); 294 return statement; 295 } 296 }, new PreparedStatementCallback<List<String>>() { 297 public List<String> doInPreparedStatement( 298 PreparedStatement statement) throws SQLException, DataAccessException { 299 List<String> routeNodeNames = new ArrayList<String>(); 300 statement.setString(1, documentId); 301 statement.setBoolean(2, Boolean.FALSE); 302 statement.setBoolean(3, Boolean.TRUE); 303 ResultSet rs = statement.executeQuery(); 304 try { 305 while (rs.next()) { 306 String name = rs.getString("nm"); 307 routeNodeNames.add(name); 308 } 309 } finally { 310 if (rs != null) { 311 rs.close(); 312 } 313 } 314 return routeNodeNames; 315 } 316 } 317 ); 318 return names; 319 } 320 321 public List getInitialNodeInstances(String documentId) { 322 //FIXME: Not sure this query is returning what it needs to 323 Query query = entityManager.createNamedQuery(FIND_INITIAL_NODE_INSTANCES_NAME); 324 query.setParameter(KEWPropertyConstants.DOCUMENT_ID, documentId); 325 return (List) query.getResultList(); 326 } 327 328 public NodeState findNodeState(Long nodeInstanceId, String key) { 329 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 330 equal(KEWPropertyConstants.ROUTE_NODE_INSTANCE_ID, nodeInstanceId.toString()), 331 equal(KEWPropertyConstants.KEY, key) 332 ); 333 334 List<NodeState> nodeStates = getDataObjectService().findMatching( 335 NodeState.class, queryByCriteria.build()).getResults(); 336 if (nodeStates != null && nodeStates.size() > 0) { 337 return nodeStates.get(0); 338 } 339 return null; 340 } 341 342 public RouteNode findRouteNodeByName(String documentTypeId, String name) { 343 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 344 equal(KEWPropertyConstants.DOCUMENT_TYPE_ID, documentTypeId), 345 equal(KEWPropertyConstants.ROUTE_NODE_NAME, name) 346 ); 347 List<RouteNode> routeNodes = getDataObjectService().findMatching( 348 RouteNode.class, queryByCriteria.build()).getResults(); 349 if (routeNodes != null && routeNodes.size() > 0) { 350 return routeNodes.get(0); 351 } 352 return null; 353 } 354 355 public List<RouteNode> findFinalApprovalRouteNodes(String documentTypeId) { 356 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 357 equal(KEWPropertyConstants.DOCUMENT_TYPE_ID, documentTypeId), 358 equal(KEWPropertyConstants.FINAL_APPROVAL, Boolean.TRUE) 359 ); 360 return getDataObjectService().findMatching(RouteNode.class, queryByCriteria.build()).getResults(); 361 } 362 363 public List findProcessNodeInstances(RouteNodeInstance process) { 364 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 365 equal(KEWPropertyConstants.PROCESS_ID, process.getRouteNodeInstanceId()) 366 ); 367 return getDataObjectService().findMatching(RouteNodeInstance.class, queryByCriteria.build()).getResults(); 368 } 369 370 public List findRouteNodeInstances(String documentId) { 371 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 372 equal(KEWPropertyConstants.DOCUMENT_ID, documentId) 373 ); 374 return getDataObjectService().findMatching(RouteNodeInstance.class, queryByCriteria.build()).getResults(); 375 } 376 377 public void deleteLinksToPreNodeInstances(RouteNodeInstance routeNodeInstance) { 378 List<RouteNodeInstance> preNodeInstances = routeNodeInstance.getPreviousNodeInstances(); 379 for (Iterator<RouteNodeInstance> preNodeInstanceIter = preNodeInstances.iterator(); preNodeInstanceIter.hasNext(); ) { 380 RouteNodeInstance preNodeInstance = (RouteNodeInstance) preNodeInstanceIter.next(); 381 List<RouteNodeInstance> nextInstances = preNodeInstance.getNextNodeInstances(); 382 nextInstances.remove(routeNodeInstance); 383 getEntityManager().merge(preNodeInstance); 384 } 385 } 386 387 public void deleteRouteNodeInstancesHereAfter(RouteNodeInstance routeNodeInstance) { 388 RouteNodeInstance rnInstance = findRouteNodeInstanceById(routeNodeInstance.getRouteNodeInstanceId()); 389 entityManager.remove(rnInstance); 390 } 391 392 public void deleteNodeStateById(Long nodeStateId) { 393 QueryByCriteria.Builder queryByCriteria = QueryByCriteria.Builder.create().setPredicates( 394 equal(KEWPropertyConstants.ROUTE_NODE_STATE_ID, nodeStateId) 395 ); 396 List<NodeState> nodeStates = getDataObjectService().findMatching( 397 NodeState.class, queryByCriteria.build()).getResults(); 398 NodeState nodeState = null; 399 if (nodeStates != null && nodeStates.size() > 0) { 400 nodeState = nodeStates.get(0); 401 } 402 getDataObjectService().delete(nodeState); 403 } 404 405 public void deleteNodeStates(List statesToBeDeleted) { 406 for (Iterator stateToBeDeletedIter = statesToBeDeleted.iterator(); stateToBeDeletedIter.hasNext(); ) { 407 Long stateId = (Long) stateToBeDeletedIter.next(); 408 deleteNodeStateById(stateId); 409 } 410 } 411 412 413 public DataObjectService getDataObjectService() { 414 return dataObjectService; 415 } 416 417 @Required 418 public void setDataObjectService(DataObjectService dataObjectService) { 419 this.dataObjectService = dataObjectService; 420 } 421 422 423}