001/** 002 * Copyright 2005-2014 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.service.impl; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.commons.lang.exception.ExceptionUtils; 020import org.kuali.rice.kew.api.KewApiConstants; 021import org.kuali.rice.kew.api.action.ActionItem; 022import org.kuali.rice.kew.docsearch.SearchableAttributeValue; 023import org.kuali.rice.kew.docsearch.dao.SearchableAttributeDAO; 024import org.kuali.rice.kew.doctype.bo.DocumentType; 025import org.kuali.rice.kew.exception.WorkflowServiceErrorException; 026import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl; 027import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 028import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent; 029import org.kuali.rice.kew.routeheader.dao.DocumentRouteHeaderDAO; 030import org.kuali.rice.kew.routeheader.service.RouteHeaderService; 031import org.kuali.rice.kew.service.KEWServiceLocator; 032import org.kuali.rice.kim.api.identity.principal.Principal; 033import org.kuali.rice.kim.api.services.KimApiServiceLocator; 034import org.kuali.rice.krad.data.DataObjectService; 035import org.kuali.rice.krad.data.PersistenceOption; 036import org.springframework.beans.factory.annotation.Required; 037 038import javax.persistence.OptimisticLockException; 039import java.math.BigDecimal; 040import java.sql.Timestamp; 041import java.util.ArrayList; 042import java.util.Collection; 043import java.util.HashMap; 044import java.util.HashSet; 045import java.util.List; 046import java.util.Map; 047import java.util.Set; 048 049public class RouteHeaderServiceImpl implements RouteHeaderService { 050 051 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RouteHeaderServiceImpl.class); 052 053 private DocumentRouteHeaderDAO routeHeaderDAO; 054 private SearchableAttributeDAO searchableAttributeDAO; 055 056 private DataObjectService dataObjectService; 057 058 public DocumentRouteHeaderValue getRouteHeader(String documentId) { 059 return getDataObjectService().find(DocumentRouteHeaderValue.class, documentId); 060 } 061 062 public DocumentRouteHeaderValue getRouteHeader(String documentId, boolean clearCache) { 063 return getRouteHeaderDAO().findRouteHeader(documentId, clearCache); 064 } 065 066 public Collection<DocumentRouteHeaderValue> getRouteHeaders(Collection<String> documentIds) { 067 return getRouteHeaderDAO().findRouteHeaders(documentIds); 068 } 069 070 public Collection<DocumentRouteHeaderValue> getRouteHeaders(Collection<String> documentIds, boolean clearCache) { 071 return getRouteHeaderDAO().findRouteHeaders(documentIds, clearCache); 072 } 073 074 public Map<String,DocumentRouteHeaderValue> getRouteHeadersForActionItems(Collection<ActionItem> actionItems) { 075 Map<String,DocumentRouteHeaderValue> routeHeaders = new HashMap<String,DocumentRouteHeaderValue>(); 076 List<String> documentIds = new ArrayList<String>(actionItems.size()); 077 for (ActionItem actionItem : actionItems) { 078 documentIds.add(actionItem.getDocumentId()); 079 } 080 Collection<DocumentRouteHeaderValue> actionItemRouteHeaders = getRouteHeaders(documentIds); 081 if (actionItemRouteHeaders != null) { 082 for (DocumentRouteHeaderValue routeHeader : actionItemRouteHeaders) { 083 routeHeaders.put(routeHeader.getDocumentId(), routeHeader); 084 } 085 } 086 return routeHeaders; 087 } 088 089 public void lockRouteHeader(String documentId) { 090 getRouteHeaderDAO().lockRouteHeader(documentId); 091 LOG.debug("Successfully locked document [docId=" + documentId + "]"); 092 } 093 094 public DocumentRouteHeaderValue saveRouteHeader(DocumentRouteHeaderValue routeHeader) { 095 if ( LOG.isDebugEnabled() ) { 096 LOG.debug( "About to Save the route Header: " + routeHeader.getDocumentId() + " / version=" 097 + routeHeader.getVersionNumber() ); 098 DocumentRouteHeaderValue currHeader = getDataObjectService().find(DocumentRouteHeaderValue.class, 099 routeHeader.getDocumentId()); 100 if ( currHeader != null ) { 101 LOG.debug( "Current Header Version: " + currHeader.getVersionNumber() ); 102 } else { 103 LOG.debug( "Current Header: null" ); 104 } 105 LOG.debug( ExceptionUtils.getStackTrace(new Throwable()) ); 106 } 107 try { 108 // before saving, copy off the document content, since it's transient it will get erased during a JPA merge 109 DocumentRouteHeaderValueContent content = routeHeader.getDocumentContent(); 110 DocumentRouteHeaderValue drvPersisted = dataObjectService.save(routeHeader, PersistenceOption.FLUSH); 111 // now let's save the content and reattach it to our document 112 content.setDocumentId(drvPersisted.getDocumentId()); 113 content = dataObjectService.save(content); 114 drvPersisted.setDocumentContent(content); 115 return drvPersisted; 116 } catch ( RuntimeException ex ) { 117 if ( ex.getCause() instanceof OptimisticLockException) { 118 LOG.error( "Optimistic Locking Exception saving document header or content. Offending object: " 119 + ex.getCause() 120 + "; DocumentId = " + routeHeader.getDocumentId() + " ; Version Number = " 121 + routeHeader.getVersionNumber()); 122 } 123 LOG.error( "Unable to save document header or content. Route Header: " + routeHeader, ex ); 124 throw ex; 125 } 126 } 127 128 public void deleteRouteHeader(DocumentRouteHeaderValue routeHeader) { 129 dataObjectService.delete(routeHeader); 130 } 131 132 public String getNextDocumentId() { 133 return getRouteHeaderDAO().getNextDocumentId(); 134 } 135 136 public Collection findPendingByResponsibilityIds(Set responsibilityIds) { 137 return getRouteHeaderDAO().findPendingByResponsibilityIds(responsibilityIds); 138 } 139 140 public void clearRouteHeaderSearchValues(String documentId) { 141 getRouteHeaderDAO().clearRouteHeaderSearchValues(documentId); 142 } 143 144 public void updateRouteHeaderSearchValues(String documentId, List<SearchableAttributeValue> searchAttributes) { 145 getRouteHeaderDAO().clearRouteHeaderSearchValues(documentId); 146 HashSet<String> dupedSet = new HashSet<String>(); 147 //"de-dupe" for value,key,and doc header id 148 for (SearchableAttributeValue searchAttribute : searchAttributes) { 149 if(searchAttribute != null){ 150 String fakeKey = searchAttribute.getSearchableAttributeKey() + "-" + searchAttribute.getSearchableAttributeValue(); 151 if(!dupedSet.contains(fakeKey)){ 152 getRouteHeaderDAO().save(searchAttribute); 153 dupedSet.add(fakeKey); 154 } 155 } 156 } 157 LOG.warn("Deduplication adjusted incoming SearchableAttributeValue list from original: " + searchAttributes.size() + " entries into : " + (searchAttributes.size() - dupedSet.size()) + " entries."); 158 } 159 160 public void validateRouteHeader(DocumentRouteHeaderValue routeHeader){ 161 LOG.debug("Enter validateRouteHeader(..)"); 162 List errors = new ArrayList(); 163 164 if (routeHeader.getDocRouteStatus() == null || routeHeader.getDocRouteStatus().trim().equals("")) { 165 errors.add(new WorkflowServiceErrorImpl("RouteHeader route status null.", "routeheader.routestatus.empty")); 166 } else if (!KewApiConstants.DOCUMENT_STATUSES.containsKey(routeHeader.getDocRouteStatus())){ 167 errors.add(new WorkflowServiceErrorImpl("RouteHeader route status invalid.", "routeheader.routestatus.invalid")); 168 } 169 170 if(routeHeader.getDocRouteLevel() == null || routeHeader.getDocRouteLevel().intValue() < 0){ 171 errors.add(new WorkflowServiceErrorImpl("RouteHeader route level invalid.", "routeheader.routelevel.invalid")); 172 } 173 174 if(routeHeader.getDateLastModified() == null){ 175 errors.add(new WorkflowServiceErrorImpl("RouteHeader status modification date empty.", "routeheader.statusmoddate.empty")); 176 } 177 178 if(routeHeader.getCreateDate() == null){ 179 errors.add(new WorkflowServiceErrorImpl("RouteHeader status create date empty.", "routeheader.createdate.empty")); 180 } 181 if(routeHeader.getDocVersion() == null || routeHeader.getDocVersion().intValue() < 0){ 182 errors.add(new WorkflowServiceErrorImpl("RouteHeader doc version invalid.", "routeheader.docversion.invalid")); 183 } 184 185 if (routeHeader.getInitiatorWorkflowId () == null || routeHeader.getInitiatorWorkflowId().trim().equals("")) { 186 errors.add(new WorkflowServiceErrorImpl("RouteHeader initiator null.", "routeheader.initiator.empty")); 187 } 188 else 189 { 190 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(routeHeader.getInitiatorWorkflowId()); 191 if(principal == null) 192 { 193 errors.add(new WorkflowServiceErrorImpl("RouteHeader initiator id invalid.", "routeheader.initiator.invalid")); 194 } 195 } 196 197 if(!StringUtils.isBlank(routeHeader.getDocumentTypeId())){ 198 DocumentType docType = KEWServiceLocator.getDocumentTypeService().findById(routeHeader.getDocumentTypeId()); 199 if(docType == null){ 200 errors.add(new WorkflowServiceErrorImpl("RouteHeader document type id invalid.", "routeheader.doctypeid.invalid")); 201 } 202 } 203 204 LOG.debug("Exit validateRouteHeader(..) "); 205 if (!errors.isEmpty()) { 206 throw new WorkflowServiceErrorException("RouteHeader Validation Error", errors); 207 } 208 } 209 210 public String getApplicationIdByDocumentId(String documentId) { 211 return getRouteHeaderDAO().getApplicationIdByDocumentId(documentId); 212 } 213 214 public DocumentRouteHeaderValueContent getContent(String documentId) { 215 if (documentId == null) { 216 return new DocumentRouteHeaderValueContent(); 217 } 218 DocumentRouteHeaderValueContent content = getRouteHeaderDAO().getContent(documentId); 219 if (content == null) { 220 content = new DocumentRouteHeaderValueContent(documentId); 221 } 222 return content; 223 } 224 225 public boolean hasSearchableAttributeValue(String documentId, String searchableAttributeKey, String searchableAttributeValue) { 226 return getRouteHeaderDAO().hasSearchableAttributeValue(documentId, searchableAttributeKey, searchableAttributeValue); 227 } 228 229 public String getDocumentStatus(String documentId) { 230 return getRouteHeaderDAO().getDocumentStatus(documentId); 231 } 232 233 public String getAppDocId(String documentId) { 234 if (documentId == null) { 235 return null; 236 } 237 return getRouteHeaderDAO().getAppDocId(documentId); 238 } 239 240 public String getAppDocStatus(String documentId) { 241 if (documentId == null) { 242 return null; 243 } 244 return getRouteHeaderDAO().getAppDocStatus(documentId); 245 } 246 247 public DocumentRouteHeaderDAO getRouteHeaderDAO() { 248 return routeHeaderDAO; 249 } 250 251 public void setRouteHeaderDAO(DocumentRouteHeaderDAO routeHeaderDAO) { 252 this.routeHeaderDAO = routeHeaderDAO; 253 } 254 255 public List<Timestamp> getSearchableAttributeDateTimeValuesByKey( 256 String documentId, String key) { 257 return getSearchableAttributeDAO().getSearchableAttributeDateTimeValuesByKey(documentId, key); 258 } 259 260 public List<BigDecimal> getSearchableAttributeFloatValuesByKey( 261 String documentId, String key) { 262 return getSearchableAttributeDAO().getSearchableAttributeFloatValuesByKey(documentId, key); 263 } 264 265 public List<Long> getSearchableAttributeLongValuesByKey(String documentId, 266 String key) { 267 return getSearchableAttributeDAO().getSearchableAttributeLongValuesByKey(documentId, key); 268 } 269 270 public List<String> getSearchableAttributeStringValuesByKey( 271 String documentId, String key) { 272 273 return getSearchableAttributeDAO().getSearchableAttributeStringValuesByKey(documentId, key); 274 } 275 276 public void setSearchableAttributeDAO(SearchableAttributeDAO searchableAttributeDAO) { 277 this.searchableAttributeDAO = searchableAttributeDAO; 278 } 279 280 public SearchableAttributeDAO getSearchableAttributeDAO() { 281 return searchableAttributeDAO; 282 } 283 284 public Collection findByDocTypeAndAppId(String documentTypeName, 285 String appId) { 286 return getRouteHeaderDAO().findByDocTypeAndAppId(documentTypeName, appId); 287 } 288 289 public DataObjectService getDataObjectService(){ 290 return dataObjectService; 291 } 292 293 @Required 294 public void setDataObjectService(DataObjectService dataObjectService) { 295 this.dataObjectService = dataObjectService; 296 } 297}