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.impl.stuck; 017 018import org.springframework.beans.factory.annotation.Required; 019 020import javax.persistence.EntityManager; 021import javax.persistence.Query; 022import javax.persistence.TypedQuery; 023import javax.persistence.criteria.CriteriaBuilder; 024import javax.persistence.criteria.CriteriaQuery; 025import javax.persistence.criteria.ParameterExpression; 026import javax.persistence.criteria.Root; 027import java.sql.Timestamp; 028import java.util.ArrayList; 029import java.util.Collections; 030import java.util.HashSet; 031import java.util.List; 032import java.util.Set; 033import java.util.stream.Collectors; 034 035/** 036 * @author Eric Westfall 037 */ 038public class StuckDocumentDaoJpa implements StuckDocumentDao { 039 040 private static final String NEW_STUCK_DOCUMENT_ACTION_ITEM_SQL = 041 "select DH.DOC_HDR_ID from KREW_DOC_HDR_T DH " + 042 "left outer join KREW_ACTN_ITM_T AI on DH.DOC_HDR_ID=AI.DOC_HDR_ID " + 043 "left outer join (select DOC_HDR_ID, STATUS, START_DT, END_DT from KREW_STUCK_DOC_INCIDENT_T where (DOC_HDR_ID, START_DT) in (select DOC_HDR_ID, MAX(START_DT) from KREW_STUCK_DOC_INCIDENT_T group by DOC_HDR_ID)) SD " + 044 "on DH.DOC_HDR_ID=SD.DOC_HDR_ID " + 045 "where DH.DOC_HDR_STAT_CD='R' AND AI.DOC_HDR_ID IS NULL and " + 046 "(SD.DOC_HDR_ID IS NULL OR SD.STATUS='FIXED' OR (SD.STATUS='FAILED' AND DH.STAT_MDFN_DT > SD.END_DT))"; 047 048 private static final String NEW_STUCK_DOCUMENT_ACTION_REQUEST_SQL = 049 "select DH.DOC_HDR_ID from KREW_DOC_HDR_T DH " + 050 "left outer join (select DOC_HDR_ID, STAT_CD from KREW_ACTN_RQST_T AR where AR.STAT_CD='A') AR on DH.DOC_HDR_ID=AR.DOC_HDR_ID " + 051 "left outer join (select DOC_HDR_ID, STATUS, START_DT, END_DT from KREW_STUCK_DOC_INCIDENT_T where (DOC_HDR_ID, START_DT) in (select DOC_HDR_ID, MAX(START_DT) from KREW_STUCK_DOC_INCIDENT_T group by DOC_HDR_ID)) as SD " + 052 "on DH.DOC_HDR_ID=SD.DOC_HDR_ID " + 053 "where DH.DOC_HDR_STAT_CD='R' and AR.DOC_HDR_ID IS NULL and " + 054 "(SD.DOC_HDR_ID IS NULL OR SD.STATUS='FIXED' OR (SD.STATUS='FAILED' AND DH.STAT_MDFN_DT > SD.END_DT))"; 055 056 private static final String ALL_STUCK_DOCUMENT_ACTION_ITEM_SQL = 057 "select DH.DOC_HDR_ID, DH.CRTE_DT, DT.LBL from KREW_DOC_HDR_T DH " + 058 "left outer join KREW_ACTN_ITM_T AI on DH.DOC_HDR_ID=AI.DOC_HDR_ID " + 059 "join KREW_DOC_TYP_T DT on DH.DOC_TYP_ID=DT.DOC_TYP_ID " + 060 "where DH.DOC_HDR_STAT_CD='R' AND AI.DOC_HDR_ID IS NULL"; 061 062 private static final String ALL_STUCK_DOCUMENT_ACTION_REQUEST_SQL = 063 "select DH.DOC_HDR_ID, DH.CRTE_DT, DT.LBL from KREW_DOC_HDR_T DH " + 064 "left outer join KREW_ACTN_RQST_T AR on DH.DOC_HDR_ID=AR.DOC_HDR_ID AND AR.STAT_CD = 'A' " + 065 "join KREW_DOC_TYP_T DT on DH.DOC_TYP_ID=DT.DOC_TYP_ID " + 066 "where DH.DOC_HDR_STAT_CD='R' AND AR.DOC_HDR_ID IS NULL"; 067 068 private static final String IS_STUCK_DOCUMENT_ACTION_ITEM_SQL = 069 ALL_STUCK_DOCUMENT_ACTION_ITEM_SQL + " AND DH.DOC_HDR_ID = ?"; 070 071 private static final String IS_STUCK_DOCUMENT_ACTION_REQUEST_SQL = 072 ALL_STUCK_DOCUMENT_ACTION_REQUEST_SQL + " AND DH.DOC_HDR_ID = ?"; 073 074 static final String FIX_ATTEMPTS_FOR_INCIDENT_NAME = "StuckDocumentFixAttempt.FixAttemptsForIncident"; 075 static final String FIX_ATTEMPTS_FOR_INCIDENT_QUERY = "select fa from StuckDocumentFixAttempt fa where fa.stuckDocumentIncidentId = :stuckDocumentIncidentId"; 076 077 078 079 private EntityManager entityManager; 080 081 @Override 082 public List<String> findAllStuckDocumentIds() { 083 return findAllStuckDocuments().stream().map(StuckDocument::getDocumentId).collect(Collectors.toList()); 084 } 085 086 @Override 087 public List<StuckDocument> findAllStuckDocuments() { 088 List<Object[]> stuckDocumentResults = new ArrayList<>(); 089 stuckDocumentResults.addAll(entityManager.createNativeQuery(ALL_STUCK_DOCUMENT_ACTION_ITEM_SQL).getResultList()); 090 stuckDocumentResults.addAll(entityManager.createNativeQuery(ALL_STUCK_DOCUMENT_ACTION_REQUEST_SQL).getResultList()); 091 List<StuckDocument> unfilteredStuckDocuments = stuckDocumentResults.stream().map(result -> new StuckDocument((String)result[0], (String)result[2], ((Timestamp)result[1]).toLocalDateTime())).collect(Collectors.toList()); 092 return filterDuplicateStuckDocuments(unfilteredStuckDocuments); 093 } 094 095 private List<StuckDocument> filterDuplicateStuckDocuments(List<StuckDocument> unfilteredStuckDocuments) { 096 Set<String> processedDocumentIds = new HashSet<>(); 097 return unfilteredStuckDocuments.stream().filter(stuckDocument -> { 098 if (processedDocumentIds.contains(stuckDocument.getDocumentId())) { 099 return false; 100 } 101 processedDocumentIds.add(stuckDocument.getDocumentId()); 102 return true; 103 }).collect(Collectors.toList()); 104 } 105 106 @Override 107 public StuckDocumentIncident findIncident(String stuckDocumentIncidentId) { 108 return entityManager.find(StuckDocumentIncident.class, stuckDocumentIncidentId); 109 } 110 111 @Override 112 public StuckDocumentIncident saveIncident(StuckDocumentIncident incident) { 113 return entityManager.merge(incident); 114 } 115 116 public void deleteIncident(StuckDocumentIncident incident) { 117 entityManager.remove(incident); 118 } 119 120 @Override 121 public List<StuckDocumentIncident> findAllIncidents(int maxIncidents) { 122 CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); 123 CriteriaQuery<StuckDocumentIncident> q = cb.createQuery(StuckDocumentIncident.class); 124 Root<StuckDocumentIncident> incident = q.from(StuckDocumentIncident.class); 125 q.select(incident).orderBy(cb.desc(incident.get("startDate"))); 126 TypedQuery<StuckDocumentIncident> query = getEntityManager().createQuery(q); 127 query.setMaxResults(maxIncidents); 128 return new ArrayList<>(query.getResultList()); 129 } 130 131 @Override 132 public List<StuckDocumentIncident> findIncidentsByStatus(int maxIncidents, StuckDocumentIncident.Status status) { 133 CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); 134 CriteriaQuery<StuckDocumentIncident> q = cb.createQuery(StuckDocumentIncident.class); 135 Root<StuckDocumentIncident> incident = q.from(StuckDocumentIncident.class); 136 q.select(incident).orderBy(cb.desc(incident.get("startDate"))); 137 ParameterExpression<StuckDocumentIncident.Status> statusParameter = cb.parameter(StuckDocumentIncident.Status.class); 138 q.where(cb.equal(incident.get("status"), statusParameter)); 139 TypedQuery<StuckDocumentIncident> query = getEntityManager().createQuery(q); 140 query.setParameter(statusParameter, status); 141 query.setMaxResults(maxIncidents); 142 return new ArrayList<>(query.getResultList()); 143 } 144 145 @Override 146 public List<StuckDocumentFixAttempt> findAllFixAttempts(String stuckDocumentIncidentId) { 147 TypedQuery<StuckDocumentFixAttempt> query = getEntityManager().createNamedQuery(FIX_ATTEMPTS_FOR_INCIDENT_NAME, StuckDocumentFixAttempt.class); 148 query.setParameter("stuckDocumentIncidentId", stuckDocumentIncidentId); 149 return new ArrayList<>(query.getResultList()); 150 } 151 152 @Override 153 public StuckDocumentFixAttempt saveFixAttempt(StuckDocumentFixAttempt auditEntry) { 154 return entityManager.merge(auditEntry); 155 } 156 157 @Override 158 public List<String> identifyNewStuckDocuments() { 159 Set<String> documentIds = new HashSet<>(); 160 documentIds.addAll(entityManager.createNativeQuery(NEW_STUCK_DOCUMENT_ACTION_ITEM_SQL).getResultList()); 161 documentIds.addAll(entityManager.createNativeQuery(NEW_STUCK_DOCUMENT_ACTION_REQUEST_SQL).getResultList()); 162 return Collections.unmodifiableList(new ArrayList<>(documentIds)); 163 } 164 165 public boolean isStuck(String documentId) { 166 Query aiQuery = entityManager.createNativeQuery(IS_STUCK_DOCUMENT_ACTION_ITEM_SQL); 167 Query arQuery = entityManager.createNativeQuery(IS_STUCK_DOCUMENT_ACTION_REQUEST_SQL); 168 aiQuery.setParameter(1, documentId); 169 arQuery.setParameter(1, documentId); 170 return !aiQuery.getResultList().isEmpty() || !arQuery.getResultList().isEmpty(); 171 } 172 173 @Override 174 public List<StuckDocumentIncident> identifyStillStuckDocuments(List<String> incidentIds) { 175 return incidentIds.stream().map(this::findIncident).filter(incident -> isStuck(incident.getDocumentId())).collect(Collectors.toList()); 176 } 177 178 public EntityManager getEntityManager() { 179 return entityManager; 180 } 181 182 @Required 183 public void setEntityManager(EntityManager entityManager) { 184 this.entityManager = entityManager; 185 } 186 187}