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.doctype.service.impl; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.jdom.Element; 020import org.kuali.rice.core.api.criteria.QueryByCriteria; 021import org.kuali.rice.core.api.criteria.QueryResults; 022import org.kuali.rice.core.api.impex.ExportDataSet; 023import org.kuali.rice.kew.doctype.bo.DocumentType; 024import org.kuali.rice.kew.doctype.dao.DocumentTypeDAO; 025import org.kuali.rice.kew.doctype.service.DocumentTypeService; 026import org.kuali.rice.kew.exception.WorkflowServiceErrorException; 027import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl; 028import org.kuali.rice.kew.rule.RuleBaseValues; 029import org.kuali.rice.kew.xml.DocumentTypeXmlParser; 030import org.kuali.rice.kew.xml.export.DocumentTypeXmlExporter; 031import org.kuali.rice.krad.data.DataObjectService; 032import org.kuali.rice.krad.data.PersistenceOption; 033import org.kuali.rice.krad.util.KRADUtils; 034import org.springframework.beans.factory.annotation.Required; 035 036import java.io.InputStream; 037import java.util.ArrayList; 038import java.util.Collection; 039import java.util.Iterator; 040import java.util.List; 041 042import static org.kuali.rice.core.api.criteria.PredicateFactory.*; 043 044/** 045 * The standard implementation of the DocumentTypeService. 046 * 047 * @author Kuali Rice Team (rice.collab@kuali.org) 048 * This class does not support KEW REMOTE mode. 049 * KULRICE-7770 added an expicit check for this class in GlobalResourceDelegatingSpringCreator.java 050 */ 051public class DocumentTypeServiceImpl implements DocumentTypeService { 052 053 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentTypeServiceImpl.class); 054 protected static final String XML_FILE_PARSE_ERROR = "general.error.parsexml"; 055 056 private DocumentTypeDAO documentTypeDAO; 057 private DataObjectService dataObjectService; 058 059 public Collection<DocumentType> find(DocumentType documentType, String docTypeParentName, boolean climbHierarchy) { 060 DocumentType docTypeParent = this.findByName(docTypeParentName); 061 return getDocumentTypeDAO().find(documentType, docTypeParent, climbHierarchy); 062 } 063 064 public DocumentType findById(String documentTypeId) { 065 if (documentTypeId == null) { 066 return null; 067 } 068 return getDataObjectService().find(DocumentType.class,documentTypeId); 069 } 070 071 public DocumentType findByDocumentId(String documentId) { 072 if (documentId == null) { 073 return null; 074 } 075 076 return documentTypeDAO.findDocumentTypeByDocumentId(documentId); 077 } 078 079 public DocumentType findByName(String name) { 080 return this.findByName(name, true); 081 } 082 083 public DocumentType findByNameCaseInsensitive(String name) { 084 return this.findByName(name, false); 085 } 086 087 /** 088 * 089 * This method seaches for a DocumentType by document name. 090 * 091 * @param name 092 * @param caseSensitive 093 * @return 094 */ 095 private DocumentType findByName(String name, boolean caseSensitive) { 096 if (name == null) { 097 return null; 098 } 099 return documentTypeDAO.findByName(name, caseSensitive); 100 } 101 102 public DocumentType versionAndSave(DocumentType documentType) { 103 // at this point this save is designed to version the document type by creating an entire new record if this is going to be an update and 104 // not a create just throw and exception to be on the safe side 105 if (documentType.getDocumentTypeId() != null && documentType.getVersionNumber() != null) { 106 throw new RuntimeException("DocumentType configured for update and not versioning which we support"); 107 } 108 109 // grab the old document. Don't Use Cached Version! 110 DocumentType oldDocumentType = findByName(documentType.getName()); 111 // reset the children on the oldDocumentType 112 //oldDocumentType.resetChildren(); 113 String existingDocTypeId = null; 114 if (oldDocumentType != null) { 115 existingDocTypeId = oldDocumentType.getDocumentTypeId(); 116 // set version number on the new doc type using the max version from the database 117 Integer maxVersionNumber = documentTypeDAO.getMaxVersionNumber(documentType.getName()); 118 documentType.setVersion((maxVersionNumber != null) ? new Integer(maxVersionNumber.intValue() + 1) : new Integer(0)); 119 oldDocumentType.setCurrentInd(Boolean.FALSE); 120 if ( LOG.isInfoEnabled() ) { 121 LOG.info("Saving old document type Id " + oldDocumentType.getDocumentTypeId() + " name '" + oldDocumentType.getName() + "' (current = " + oldDocumentType.getCurrentInd() + ")"); 122 } 123 oldDocumentType = save(oldDocumentType); 124 } 125 // check to see that no current documents exist in database 126 if (!CollectionUtils.isEmpty(documentTypeDAO.findAllCurrentByName(documentType.getName()))) { 127 String errorMsg = "Found invalid 'current' document with name '" + documentType.getName() + "'. None should exist."; 128 LOG.error(errorMsg); 129 throw new RuntimeException(errorMsg); 130 } 131 // set up the previous current doc type on the new doc type 132 documentType.setPreviousVersionId(existingDocTypeId); 133 documentType.setCurrentInd(Boolean.TRUE); 134 documentType = save(documentType); 135 if ( LOG.isInfoEnabled() ) { 136 LOG.info("Saved current document type Id " + documentType.getDocumentTypeId() + " name '" + documentType.getName() + "' (current = " + documentType.getCurrentInd() + ")"); 137 } 138 //attach the children to this new parent. cloning the children would probably be a better way to go here... 139 if (existingDocTypeId != null) { 140 // documentType.getPreviousVersion() should not be null at this point 141 for (Iterator iterator = getChildDocumentTypes(existingDocTypeId).iterator(); iterator.hasNext();) { 142// for (Iterator iterator = oldDocumentType.getChildrenDocTypes().iterator(); iterator.hasNext();) { 143 DocumentType child = (DocumentType) iterator.next(); 144 child.setDocTypeParentId(documentType.getDocumentTypeId()); 145 save(child); 146 if ( LOG.isInfoEnabled() ) { 147 LOG.info("Saved child document type Id " + child.getDocumentTypeId() + " name '" + child.getName() + "' (parent = " + child.getDocTypeParentId() + ", current = " + child.getCurrentInd() + ")"); 148 } 149 } 150 } 151 // initiate a save of this document type's parent document type, this will force a 152 // version check which should reveal (via an optimistic lock exception) whether or 153 // not there is a concurrent transaction 154 // which has modified the parent (and therefore made it non-current) 155 // be sure to get the parent doc type directly from the db and not from the cache 156 if (documentType.getDocTypeParentId() != null) { 157 DocumentType parent = getDataObjectService().find(DocumentType.class,documentType.getDocTypeParentId()); 158 save(parent); 159 if ( LOG.isInfoEnabled() ) { 160 LOG.info("Saved parent document type Id " + parent.getDocumentTypeId() + " name '" + parent.getName() + "' (current = " + parent.getCurrentInd() + ")"); 161 } 162 } 163 return documentType; 164 } 165 166 public String findParentNameByName(String documentTypeName) { 167 return getDocumentTypeDAO().findParentNameByName(documentTypeName); 168 } 169 170 public DocumentType save(DocumentType documentType) { 171 // any time that we save a document type, let's grab an optimistic lock on it's parent document type, that's 172 // because perhaps another transaction created a new version of the parent doc type, if we don't optimistic 173 // lock and fail this transaction, then we will end up linking to a non-current parent version! 174 if (documentType.getParentId() != null) { 175 getDocumentTypeDAO().incrementOptimisticLock(documentType.getParentId()); 176 } 177 return getDataObjectService().save(documentType, PersistenceOption.FLUSH); 178 } 179 180 public DocumentTypeDAO getDocumentTypeDAO() { 181 return documentTypeDAO; 182 } 183 184 public void setDocumentTypeDAO(DocumentTypeDAO documentTypeDAO) { 185 this.documentTypeDAO = documentTypeDAO; 186 } 187 188 public synchronized List findAllCurrentRootDocuments() { 189 QueryByCriteria.Builder builder = QueryByCriteria.Builder.create(); 190 builder.setPredicates( 191 isNull("docTypeParentId"), 192 equal("currentInd", Boolean.TRUE) 193 ); 194 QueryResults<DocumentType> results = getDataObjectService().findMatching(DocumentType.class, builder.build()); 195 return results.getResults(); 196 } 197 198 public List findAllCurrent() { 199 QueryByCriteria.Builder builder = QueryByCriteria.Builder.create(); 200 builder.setPredicates( 201 equal("currentInd", Boolean.TRUE) 202 ); 203 QueryResults<DocumentType> results = getDataObjectService().findMatching(DocumentType.class, builder.build()); 204 return results.getResults(); 205 } 206 207 public List<DocumentType> findPreviousInstances(String documentTypeName) { 208 QueryByCriteria.Builder builder = QueryByCriteria.Builder.create(); 209 builder.setPredicates( 210 equal("name",documentTypeName), 211 equal("currentInd", Boolean.FALSE) 212 ); 213 QueryResults<DocumentType> results = getDataObjectService().findMatching(DocumentType.class, builder.build()); 214 return results.getResults(); 215 } 216 217 public DocumentType findRootDocumentType(DocumentType docType) { 218 if (docType.getParentDocType() != null) { 219 return findRootDocumentType(docType.getParentDocType()); 220 } else { 221 return docType; 222 } 223 } 224 225 public void loadXml(InputStream inputStream, String principalId) { 226 DocumentTypeXmlParser parser = new DocumentTypeXmlParser(); 227 try { 228 parser.parseDocumentTypes(inputStream); 229 } catch (Exception e) { 230 WorkflowServiceErrorException wsee = new WorkflowServiceErrorException("Error parsing documentType XML file", new WorkflowServiceErrorImpl("Error parsing documentType XML file", XML_FILE_PARSE_ERROR)); 231 wsee.initCause(e); 232 throw wsee; 233 } 234 } 235 236 public Element export(ExportDataSet dataSet) { 237 DocumentTypeXmlExporter exporter = new DocumentTypeXmlExporter(); 238 return exporter.export(dataSet); 239 } 240 241 @Override 242 public boolean supportPrettyPrint() { 243 return true; 244 } 245 246 public List getChildDocumentTypes(String documentTypeId) { 247 List childDocumentTypes = new ArrayList(); 248 List childIds = getDocumentTypeDAO().getChildDocumentTypeIds(documentTypeId); 249 for (Iterator iter = childIds.iterator(); iter.hasNext();) { 250 String childDocumentTypeId = (String) iter.next(); 251 childDocumentTypes.add(findById(childDocumentTypeId)); 252 } 253 return childDocumentTypes; 254 } 255 256 257 public DataObjectService getDataObjectService() { 258 return dataObjectService; 259 } 260 261 @Required 262 public void setDataObjectService(DataObjectService dataObjectService) { 263 this.dataObjectService = dataObjectService; 264 } 265 266}