001/** 002 * Copyright 2005-2015 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.routeheader.dao.impl; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.config.property.ConfigContext; 020import org.kuali.rice.kew.docsearch.SearchableAttributeValue; 021import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 022import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent; 023import org.kuali.rice.kew.routeheader.dao.DocumentRouteHeaderDAO; 024import org.kuali.rice.krad.data.DataObjectService; 025import org.kuali.rice.krad.data.platform.MaxValueIncrementerFactory; 026import org.springframework.beans.factory.annotation.Required; 027import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer; 028 029import javax.persistence.EntityManager; 030import javax.persistence.LockModeType; 031import javax.persistence.Query; 032import javax.persistence.TypedQuery; 033import javax.sql.DataSource; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041public class DocumentRouteHeaderDAOJpa implements DocumentRouteHeaderDAO { 042 043 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentRouteHeaderDAOJpa.class); 044 045 private static final String LOCK_TIMEOUT_HINT = "javax.persistence.lock.timeout"; 046 private static final Long DEFAULT_LOCK_TIMEOUT_SECONDS = Long.valueOf(60 * 60); // default to 1 hour 047 048 public static final String GET_APP_DOC_ID_NAME = "DocumentRouteHeaderValue.GetAppDocId"; 049 public static final String GET_APP_DOC_ID_QUERY = "SELECT d.appDocId from DocumentRouteHeaderValue " 050 + "as d where d.documentId = :documentId"; 051 public static final String GET_APP_DOC_STATUS_NAME = "DocumentRouteHeaderValue.GetAppDocStatus"; 052 public static final String GET_APP_DOC_STATUS_QUERY = "SELECT d.appDocStatus from " 053 + "DocumentRouteHeaderValue as d where d.documentId = :documentId"; 054 public static final String GET_DOCUMENT_HEADERS_NAME = "DocumentRouteHeaderValue.GetDocumentHeaders"; 055 public static final String GET_DOCUMENT_HEADERS_QUERY = "SELECT d from DocumentRouteHeaderValue " 056 + "as d where d.documentId IN :documentIds"; 057 public static final String GET_DOCUMENT_STATUS_NAME = "DocumentRouteHeaderValue.GetDocumentStatus"; 058 public static final String GET_DOCUMENT_STATUS_QUERY = "SELECT d.docRouteStatus from " 059 + "DocumentRouteHeaderValue as d where d.documentId = :documentId"; 060 public static final String GET_DOCUMENT_ID_BY_DOC_TYPE_APP_ID_NAME = 061 "DocumentRouteHeaderValue.GetDocumentIdByDocTypeAndAppId"; 062 public static final String GET_DOCUMENT_ID_BY_DOC_TYPE_APP_ID_QUERY = "SELECT " 063 + "DISTINCT(DH.documentId) FROM DocumentRouteHeaderValue DH, DocumentType DT " 064 + "WHERE DH.appDocId = :appDocId AND DH.documentTypeId = DT.documentTypeId AND DT.name = :name"; 065 066 private EntityManager entityManager; 067 private DataSource dataSource; 068 069 private DataObjectService dataObjectService; 070 071 @Override 072 public Collection<DocumentRouteHeaderValue> findRouteHeaders(Collection<String> documentIds){ 073 if (documentIds.isEmpty()) { 074 return new ArrayList<DocumentRouteHeaderValue>(); 075 } 076 TypedQuery<DocumentRouteHeaderValue> query = getEntityManager(). 077 createNamedQuery(GET_DOCUMENT_HEADERS_NAME, DocumentRouteHeaderValue.class); 078 query.setParameter("documentIds",documentIds); 079 return query.getResultList(); 080 } 081 082 @Override 083 public Collection<DocumentRouteHeaderValue> findRouteHeaders(Collection<String> documentIds, boolean clearCache){ 084 Collection<DocumentRouteHeaderValue> documentRouteHeaderValues = findRouteHeaders(documentIds); 085 if(clearCache){ 086 for(DocumentRouteHeaderValue drhv : documentRouteHeaderValues){ 087 getEntityManager().refresh(drhv); 088 } 089 090 } 091 return documentRouteHeaderValues; 092 } 093 094 @Override 095 public void lockRouteHeader(final String documentId) { 096 // passing a hint here on the lock timeout, this will really only work on Oracle since it supports "wait" 097 // on a SELECT ... FOR UPDATE but other databases don't 098 // 099 // one random additional piece of trivia to note, if the timeout comes back non-zero, then EclipseLink just 100 // ingores it when sending it to MySQL and issues a plain SELECT ... FOR UPDATE. However if it cames back as 0, 101 // it will try to issue a SELECT ... FOR UPDATE NOWAIT even thouh MySQL doesn't support it. This, of course, 102 // triggers an exception from the MySQL database 103 // 104 // the moral of the story? don't ever set the timeout to zero, at least not until EclipseLink fixes that bug 105 Map<String, Object> options = new HashMap<String, Object>(); 106 options.put(LOCK_TIMEOUT_HINT, getTimeoutMilliseconds()); 107 getEntityManager().find(DocumentRouteHeaderValue.class, documentId, LockModeType.PESSIMISTIC_WRITE, options); 108 } 109 110 protected Long getTimeoutMilliseconds() { 111 Long secondsToWait = DEFAULT_LOCK_TIMEOUT_SECONDS; 112 String timeoutValue = ConfigContext.getCurrentContextConfig().getDocumentLockTimeout(); 113 if (timeoutValue != null) { 114 try { 115 secondsToWait = Long.parseLong(timeoutValue); 116 } catch (NumberFormatException e) { 117 LOG.warn("Failed to parse document lock timeout as it was not a valid number: " + timeoutValue); 118 } 119 } 120 return secondsToWait * 1000; 121 } 122 123 @Override 124 public DocumentRouteHeaderValue findRouteHeader(String documentId, boolean clearCache) { 125 DocumentRouteHeaderValue dv = getDataObjectService().find(DocumentRouteHeaderValue.class,documentId); 126 if(clearCache){ 127 getEntityManager().refresh(dv); 128 } 129 return dv; 130 } 131 132 @Override 133 public String getNextDocumentId(){ 134 DataFieldMaxValueIncrementer incrementer = MaxValueIncrementerFactory.getIncrementer( 135 getDataSource(), "KREW_DOC_HDR_S"); 136 return incrementer.nextStringValue(); 137 } 138 139 public Collection<String> findPendingByResponsibilityIds(Set<String> responsibilityIds) { 140 List<String> documentIds = new ArrayList<String>(); 141 if (responsibilityIds.isEmpty()) { 142 return documentIds; 143 } 144 TypedQuery<String> query = 145 getEntityManager().createNamedQuery("ActionRequestValue.FindPendingByResponsibilityIds", String.class); 146 query.setParameter("respIds", responsibilityIds); 147 return query.getResultList(); 148 } 149 150 public void clearRouteHeaderSearchValues(String documentId) { 151 152 Query query = getEntityManager(). 153 createNamedQuery("SearchableAttributeValue.FindSearchableAttributesByDocumentId"); 154 query.setParameter("documentId",documentId); 155 List<SearchableAttributeValue> searchableAttributeValues = 156 (List<SearchableAttributeValue>)query.getResultList(); 157 for(SearchableAttributeValue sa : searchableAttributeValues){ 158 getDataObjectService().delete(sa); 159 } 160 } 161 162 @Override 163 public Collection<SearchableAttributeValue> findSearchableAttributeValues(String documentId) { 164 Query query = getEntityManager().createNamedQuery( 165 "SearchableAttributeValue.FindSearchableAttributesByDocumentId"); 166 query.setParameter("documentId",documentId); 167 return query.getResultList(); 168 } 169 170 @Override 171 public DocumentRouteHeaderValueContent getContent(String documentId) { 172 DocumentRouteHeaderValueContent content = null; 173 Query query = getEntityManager().createNamedQuery("DocumentRouteHeaderValueContent.FindByDocumentId"); 174 query.setParameter("documentId",documentId); 175 if(query.getResultList() != null && !query.getResultList().isEmpty()) { 176 content = (DocumentRouteHeaderValueContent)query.getResultList().get(0); 177 } 178 return content; 179 } 180 181 @Override 182 public boolean hasSearchableAttributeValue(String documentId, String searchableAttributeKey, String searchableAttributeValue) { 183 Query query = getEntityManager().createNamedQuery("SearchableAttributeValue.HasSearchableAttributeValue"); 184 query.setParameter("documentId",documentId); 185 query.setParameter("searchableAttributeKey",searchableAttributeKey); 186 187 if(query.getResultList() != null && !query.getResultList().isEmpty()){ 188 for(Object ob : query.getResultList()){ 189 SearchableAttributeValue sav = (SearchableAttributeValue)ob; 190 if (StringUtils.equals(sav.getSearchableAttributeDisplayValue(), searchableAttributeValue)) { 191 return true; 192 } 193 } 194 } 195 return false; 196 } 197 198 public String getDocumentStatus(String documentId) { 199 String status = null; 200 201 Query query = getEntityManager().createNamedQuery(GET_DOCUMENT_STATUS_NAME); 202 query.setParameter("documentId",documentId); 203 if(query.getResultList() != null && !query.getResultList().isEmpty()){ 204 status = (String)query.getResultList().get(0); 205 } 206 return status; 207 } 208 209 @Override 210 public void save(SearchableAttributeValue searchableAttribute) { 211 getDataObjectService().save(searchableAttribute); 212 } 213 214 public String getAppDocId(String documentId) { 215 TypedQuery<String> query = getEntityManager().createNamedQuery(GET_APP_DOC_ID_NAME,String.class 216 ); 217 query.setParameter("documentId",documentId); 218 219 String applicationDocId = null; 220 if(query.getResultList() != null && !query.getResultList().isEmpty()){ 221 applicationDocId = query.getResultList().get(0); 222 } 223 return applicationDocId; 224 225 } 226 227 public String getApplicationIdByDocumentId(String documentId) { 228 if (documentId == null) { 229 throw new IllegalArgumentException("Encountered a null document ID."); 230 } 231 232 String applicationId = null; 233 234 TypedQuery<String> query = getEntityManager().createNamedQuery( 235 "DocumentType.GetAppIdByDocumentId",String.class); 236 query.setParameter("documentId",documentId); 237 if(query.getResultList() != null && !query.getResultList().isEmpty()){ 238 applicationId = query.getResultList().get(0); 239 } 240 return applicationId; 241 242 } 243 244 public String getAppDocStatus(String documentId) { 245 String applicationDocumentStatus = null; 246 247 TypedQuery<String> query = getEntityManager().createNamedQuery(GET_APP_DOC_STATUS_NAME,String.class); 248 query.setParameter("documentId",documentId); 249 if(query.getResultList() != null && !query.getResultList().isEmpty()){ 250 applicationDocumentStatus = query.getResultList().get(0); 251 } 252 return applicationDocumentStatus; 253 } 254 255 public Collection findByDocTypeAndAppId(String documentTypeName, 256 String appId) { 257 TypedQuery<String> query = getEntityManager().createNamedQuery(GET_DOCUMENT_ID_BY_DOC_TYPE_APP_ID_NAME, 258 String.class); 259 query.setParameter("appDocId",appId); 260 query.setParameter("name",documentTypeName); 261 return query.getResultList(); 262 } 263 264 /** 265 * @return the entityManager 266 */ 267 public EntityManager getEntityManager() { 268 return this.entityManager; 269 } 270 271 /** 272 * @param entityManager the entityManager to set 273 */ 274 public void setEntityManager(EntityManager entityManager) { 275 this.entityManager = entityManager; 276 } 277 278 public DataSource getDataSource() { 279 return dataSource; 280 } 281 282 public void setDataSource(DataSource dataSource) { 283 this.dataSource = dataSource; 284 } 285 286 public DataObjectService getDataObjectService() { 287 return dataObjectService; 288 } 289 290 @Required 291 public void setDataObjectService(DataObjectService dataObjectService) { 292 this.dataObjectService = dataObjectService; 293 } 294 295}