Coverage Report - org.kuali.rice.kew.doctype.service.impl.DocumentTypeServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentTypeServiceImpl
0%
0/135
0%
0/58
2.444
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.doctype.service.impl;
 18  
 
 19  
 import java.io.InputStream;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collection;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 
 25  
 import org.apache.commons.collections.CollectionUtils;
 26  
 import org.jdom.Element;
 27  
 import org.kuali.rice.core.api.impex.ExportDataSet;
 28  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 29  
 import org.kuali.rice.kew.doctype.dao.DocumentTypeDAO;
 30  
 import org.kuali.rice.kew.doctype.service.DocumentTypePermissionService;
 31  
 import org.kuali.rice.kew.doctype.service.DocumentTypeService;
 32  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
 33  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
 34  
 import org.kuali.rice.kew.rule.bo.RuleAttribute;
 35  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 36  
 import org.kuali.rice.kew.xml.DocumentTypeXmlParser;
 37  
 import org.kuali.rice.kew.xml.export.DocumentTypeXmlExporter;
 38  
 import org.kuali.rice.krad.util.ObjectUtils;
 39  
 import org.kuali.rice.ksb.api.KsbApiServiceLocator;
 40  
 
 41  
 
 42  
 /**
 43  
  * The standard implementation of the DocumentTypeService.
 44  
  *
 45  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 46  
  */
 47  0
 public class DocumentTypeServiceImpl implements DocumentTypeService {
 48  
 
 49  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentTypeServiceImpl.class);
 50  
     protected static final String XML_FILE_PARSE_ERROR = "general.error.parsexml";
 51  
 
 52  
     public static final String DOCUMENT_TYPE_ID_CACHE_GROUP = "DocumentTypeId";
 53  
     public static final String DOCUMENT_TYPE_NAME_CACHE_GROUP = "DocumentTypeName";
 54  
 
 55  
     public static final String DOCUMENT_TYPE_ID_CACHE_PREFIX = DOCUMENT_TYPE_ID_CACHE_GROUP + ":";
 56  
     public static final String DOCUMENT_TYPE_NAME_CACHE_PREFIX = DOCUMENT_TYPE_NAME_CACHE_GROUP + ":";
 57  
     public static final String CURRENT_ROOTS_IN_CACHE_KEY = "DocumentType:CurrentRootsInCache";
 58  
 
 59  
 
 60  
     private DocumentTypeDAO documentTypeDAO;
 61  
 
 62  
     public Collection<DocumentType> find(DocumentType documentType, String docTypeParentName, boolean climbHierarchy) {
 63  0
         DocumentType docTypeParent = this.findByName(docTypeParentName);
 64  0
         Collection<DocumentType> documentTypes = getDocumentTypeDAO().find(documentType, docTypeParent, climbHierarchy);
 65  
         //since we're here put them in the cache
 66  0
         for (Object documentType1 : documentTypes)
 67  
         {
 68  0
             insertIntoCache((DocumentType) documentType1);
 69  
         }
 70  0
         return documentTypes;
 71  
     }
 72  
 
 73  
     public DocumentType findById(String documentTypeId) {
 74  0
             if (documentTypeId == null) {
 75  0
                     return null;
 76  
             }
 77  0
             DocumentType documentType = fetchFromCacheById(documentTypeId);
 78  0
             if (documentType == null) {
 79  0
                     documentType = getDocumentTypeDAO().findById(documentTypeId);
 80  0
                     insertIntoCache(documentType);
 81  
             }
 82  0
         return documentType;
 83  
     }
 84  
 
 85  
     public DocumentType findByDocumentId(String documentId) {
 86  0
             if (documentId == null) {
 87  0
                     return null;
 88  
             }
 89  0
             String documentTypeId = getDocumentTypeDAO().findDocumentTypeIdByDocumentId(documentId);
 90  0
             return findById(documentTypeId);
 91  
     }
 92  
 
 93  
     public DocumentType findByName(String name) {
 94  0
             return this.findByName(name, true, true);
 95  
     }
 96  
 
 97  
     public DocumentType findByNameCaseInsensitive(String name) {
 98  0
             return this.findByName(name, false,true);
 99  
     }
 100  
 
 101  
     /**
 102  
      * 
 103  
      * This method seaches for a DocumentType by document name.
 104  
      * 
 105  
      * @param name
 106  
      * @param caseSensitive
 107  
      * @deprecated Use findByName(String name, boolean caseSensitive, boolean checkCache)
 108  
      * @return
 109  
      */
 110  
     protected DocumentType findByName(String name, boolean caseSensitive) {
 111  0
             if (name == null) {
 112  0
                     return null;
 113  
             }
 114  0
             DocumentType documentType = fetchFromCacheByName(name);
 115  0
         if (documentType == null) {
 116  0
                 documentType = getDocumentTypeDAO().findByName(name, caseSensitive);
 117  0
                 insertIntoCache(documentType);
 118  
         }
 119  0
             return documentType;
 120  
     }
 121  
 
 122  
     /**
 123  
      * 
 124  
      * This method seaches for a DocumentType by document name.
 125  
      * 
 126  
      * @param name DocumentType name
 127  
      * @param caseSensitive If false, case will be ignored
 128  
      * @param checkCache if false the cache will not be checked.
 129  
      * @return
 130  
      */
 131  
     protected DocumentType findByName(String name, boolean caseSensitive, boolean checkCache) {
 132  0
             if (name == null) {
 133  0
                     return null;
 134  
             }
 135  
             
 136  0
             DocumentType documentType = null;
 137  0
             if(checkCache){
 138  0
                     documentType = fetchFromCacheByName(name);
 139  
             }
 140  0
         if (documentType == null) {
 141  0
                 documentType = getDocumentTypeDAO().findByName(name, caseSensitive);
 142  0
                 insertIntoCache(documentType);
 143  
         }
 144  0
             return documentType;
 145  
     }
 146  
 
 147  
     /**
 148  
      * Fetches the DocumentType from the cache with the given document type name.  If there is no entry in the cache for the given
 149  
      * document type name, null is returned.
 150  
      */
 151  
     protected DocumentType fetchFromCacheByName(String documentTypeName) {
 152  0
             return (DocumentType) KsbApiServiceLocator.getCacheAdministrator().getFromCache(getNameCacheKey(documentTypeName));
 153  
     }
 154  
 
 155  
     /**
 156  
      * Fetches the DocumentType from the cache with the given document type id.  If there is no entry in the cache for the given
 157  
      * document type id, null is returned.
 158  
      */
 159  
     protected DocumentType fetchFromCacheById(String documentTypeId) {
 160  0
             return (DocumentType) KsbApiServiceLocator.getCacheAdministrator().getFromCache(getIdCacheKey(documentTypeId));
 161  
     }
 162  
 
 163  
     /**
 164  
      * Returns the cache key for the given document type ID.
 165  
      */
 166  
     protected String getIdCacheKey(String documentTypeId) {
 167  0
             return DOCUMENT_TYPE_ID_CACHE_PREFIX + documentTypeId;
 168  
     }
 169  
 
 170  
     /**
 171  
      * Returns the cache key for the given document type name.
 172  
      */
 173  
     protected String getNameCacheKey(String documentTypeName) {
 174  0
             return DOCUMENT_TYPE_NAME_CACHE_PREFIX + documentTypeName;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Inserts the given DocumentType into the name and id caches.  If the DocumentType is already in the cache,
 179  
      * these entries should  be overwritten.
 180  
      *
 181  
      * <p>If the given DocumentType does not represent the current version of the DocumentType then it
 182  
      * should not be inserted into the name cache.  This is because different versions of DocumentTypes have
 183  
      * different IDs but they all have the same name.  We want only the most recent version of the DocumentType
 184  
      * to be cached by name.
 185  
      */
 186  
     protected void insertIntoCache(DocumentType documentType) {
 187  0
             if (documentType == null) {
 188  0
                     return;
 189  
             }
 190  
             //don't cache by name if this isn't the current version
 191  0
             if (documentType.getCurrentInd().booleanValue()) {
 192  0
                     KsbApiServiceLocator.getCacheAdministrator().putInCache(getNameCacheKey(documentType.getName()), documentType, DOCUMENT_TYPE_NAME_CACHE_GROUP);
 193  
             }
 194  
 
 195  0
             KsbApiServiceLocator.getCacheAdministrator().putInCache(getIdCacheKey(documentType.getDocumentTypeId()), documentType, DOCUMENT_TYPE_ID_CACHE_GROUP);
 196  0
     }
 197  
 
 198  
     /**
 199  
      * Flushes all DocumentTypes from the cache.
 200  
      */
 201  
     public void flushCache() {
 202  
             // invalidate locally because if we're doing an upload of a document hierarchy we can't wait the 5 secs for this nodes cache
 203  
                 //to be accurate-the data going in the db depends on it being accurate now.  This means the cache will be cleared multiple times
 204  
             //over during an upload and the subsequent notification to this node.
 205  0
             LOG.info("clearing DocumentType cache because of local update");
 206  0
             KsbApiServiceLocator.getCacheAdministrator().flushGroup(DOCUMENT_TYPE_ID_CACHE_GROUP);
 207  0
             KsbApiServiceLocator.getCacheAdministrator().flushGroup(DOCUMENT_TYPE_NAME_CACHE_GROUP);
 208  0
             KsbApiServiceLocator.getCacheAdministrator().flushGroup(DocumentTypePermissionService.DOC_TYPE_PERM_CACHE_GROUP);
 209  0
             KsbApiServiceLocator.getCacheAdministrator().flushEntry(CURRENT_ROOTS_IN_CACHE_KEY);
 210  0
     }
 211  
 
 212  
     public void clearCacheForAttributeUpdate(RuleAttribute ruleAttribute) {
 213  0
             if (ruleAttribute.getRuleAttributeId() != null) {
 214  0
                     List documentTypeAttributes = this.documentTypeDAO.findDocumentTypeAttributes(ruleAttribute);
 215  0
                     if (documentTypeAttributes.size() != 0) {
 216  0
                             flushCache();
 217  
                     }
 218  
             }
 219  0
     }
 220  
 
 221  
     public void versionAndSave(DocumentType documentType) {
 222  
             try {
 223  
                     // 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
 224  
                     // not a create just throw and exception to be on the safe side
 225  0
                     if (documentType.getDocumentTypeId() != null && documentType.getVersionNumber() != null) {
 226  0
                             throw new RuntimeException("DocumentType configured for update and not versioning which we support");
 227  
                     }
 228  
 
 229  
                     // grab the old document. Don't Use Cached Version!
 230  0
                     DocumentType oldDocumentType = findByName(documentType.getName(), true, false);
 231  
                     // reset the children on the oldDocumentType
 232  
                     //oldDocumentType.resetChildren();
 233  0
                     String existingDocTypeId = null;
 234  0
                     if (oldDocumentType != null) {
 235  0
                             existingDocTypeId = oldDocumentType.getDocumentTypeId();
 236  
                             // set version number on the new doc type using the max version from the database
 237  0
                             Integer maxVersionNumber = documentTypeDAO.getMaxVersionNumber(documentType.getName());
 238  0
                             documentType.setVersion((maxVersionNumber != null) ? new Integer(maxVersionNumber.intValue() + 1) : new Integer(0));
 239  0
                             oldDocumentType.setCurrentInd(Boolean.FALSE);
 240  0
                             if ( LOG.isInfoEnabled() ) { 
 241  0
                                     LOG.info("Saving old document type Id " + oldDocumentType.getDocumentTypeId() + " name '" + oldDocumentType.getName() + "' (current = " + oldDocumentType.getCurrentInd() + ")");
 242  
                             }
 243  0
                             save(oldDocumentType, false);
 244  
                     }
 245  
                     // check to see that no current documents exist in database
 246  0
                     if (!CollectionUtils.isEmpty(documentTypeDAO.findAllCurrentByName(documentType.getName()))) {
 247  0
                         String errorMsg = "Found invalid 'current' document with name '" + documentType.getName() + "'.  None should exist.";
 248  0
                         LOG.error(errorMsg);
 249  0
                         throw new RuntimeException(errorMsg);
 250  
                     }
 251  
             // set up the previous current doc type on the new doc type
 252  0
             documentType.setPreviousVersionId(existingDocTypeId);
 253  0
             documentType.setCurrentInd(Boolean.TRUE);
 254  0
                     save(documentType, false);
 255  0
                     if ( LOG.isInfoEnabled() ) { 
 256  0
                             LOG.info("Saved current document type Id " + documentType.getDocumentTypeId() + " name '" + documentType.getName() + "' (current = " + documentType.getCurrentInd() + ")");
 257  
                     }
 258  
                     //attach the children to this new parent.  cloning the children would probably be a better way to go here...
 259  0
                     if (ObjectUtils.isNotNull(existingDocTypeId)) {
 260  
                         // documentType.getPreviousVersion() should not be null at this point
 261  0
                 for (Iterator iterator = getChildDocumentTypes(existingDocTypeId).iterator(); iterator.hasNext();) {
 262  
 //                            for (Iterator iterator = oldDocumentType.getChildrenDocTypes().iterator(); iterator.hasNext();) {
 263  0
                                     DocumentType child = (DocumentType) iterator.next();
 264  0
                                     child.setDocTypeParentId(documentType.getDocumentTypeId());
 265  0
                                     save(child, false);
 266  0
                                     if ( LOG.isInfoEnabled() ) { 
 267  0
                                             LOG.info("Saved child document type Id " + child.getDocumentTypeId() + " name '" + child.getName() + "' (parent = " + child.getDocTypeParentId() + ", current = " + child.getCurrentInd() + ")");
 268  
                                     }
 269  0
                             }
 270  
                     }
 271  
                     // initiate a save of this document type's parent document type, this will force a
 272  
                     // version check which should reveal (via an optimistic lock exception) whether or
 273  
                     // not there is a concurrent transaction
 274  
                     // which has modified the parent (and therefore made it non-current)
 275  
                     // be sure to get the parent doc type directly from the db and not from the cache
 276  0
                     if (documentType.getDocTypeParentId() != null) {
 277  0
                             DocumentType parent = getDocumentTypeDAO().findById(documentType.getDocTypeParentId());
 278  0
                             save(parent, false);
 279  0
                             if ( LOG.isInfoEnabled() ) { 
 280  0
                                     LOG.info("Saved parent document type Id " + parent.getDocumentTypeId() + " name '" + parent.getName() + "' (current = " + parent.getCurrentInd() + ")");
 281  
                             }
 282  
                     }
 283  
 
 284  
                     // finally, flush the cache and notify the rule cache of the DocumentType change
 285  0
                     flushCache();
 286  0
                     KEWServiceLocator.getRuleService().notifyCacheOfDocumentTypeChange(documentType);
 287  
             } finally {
 288  
                     // the double flush here is necessary because of a series of events which occur inside of
 289  
                     // notifyCacheOfDocumentTypeChange, see the documentation inside that service method for
 290  
                     // more information on the problem.  Essentially, the method ends up invoking methods on
 291  
                     // this service which re-cache document types, however the document types that get
 292  
                     // re-cached are ones pulled from the OJB cache that don't have the proper children
 293  
                     // on them
 294  
                     //
 295  
                     // also we flush in the finally block because if an exception is thrown then it's still possible
 296  
                     // the the "oldDocumentType" which was fetched from the cache has had it's dbLockVerNbr incremented
 297  0
                     flushCache();
 298  0
             }
 299  0
     }
 300  
 
 301  
     public void save(DocumentType documentType, boolean flushCache) {
 302  0
             getDocumentTypeDAO().save(documentType);
 303  0
             if (flushCache) {
 304  
                     // always clear the entire cache
 305  0
                     flushCache();
 306  0
                 KEWServiceLocator.getRuleService().notifyCacheOfDocumentTypeChange(documentType);
 307  0
                 flushCache();
 308  
             }
 309  0
     }
 310  
 
 311  
     public void save(DocumentType documentType) {
 312  0
             save(documentType, true);
 313  0
     }
 314  
 
 315  
     public DocumentTypeDAO getDocumentTypeDAO() {
 316  0
         return documentTypeDAO;
 317  
     }
 318  
 
 319  
     public void setDocumentTypeDAO(DocumentTypeDAO documentTypeDAO) {
 320  0
         this.documentTypeDAO = documentTypeDAO;
 321  0
     }
 322  
 
 323  
     public synchronized List findAllCurrentRootDocuments() {
 324  0
             List currentRootsInCache = (List) KsbApiServiceLocator.getCacheAdministrator().getFromCache(CURRENT_ROOTS_IN_CACHE_KEY);
 325  
             //we can do this because we whack the entire cache when a new document type comes into the picture.
 326  0
             if (currentRootsInCache == null) {
 327  0
                     currentRootsInCache = getDocumentTypeDAO().findAllCurrentRootDocuments();
 328  0
                     KsbApiServiceLocator.getCacheAdministrator().putInCache(CURRENT_ROOTS_IN_CACHE_KEY, currentRootsInCache);
 329  
             }
 330  0
             return currentRootsInCache;
 331  
     }
 332  
 
 333  
     public List findAllCurrent() {
 334  0
         return getDocumentTypeDAO().findAllCurrent();
 335  
     }
 336  
 
 337  
     public List<DocumentType> findPreviousInstances(String documentTypeName) {
 338  0
         return getDocumentTypeDAO().findPreviousInstances(documentTypeName);
 339  
     }
 340  
 
 341  
     public DocumentType findRootDocumentType(DocumentType docType) {
 342  0
         if (docType.getParentDocType() != null) {
 343  0
             return findRootDocumentType(docType.getParentDocType());
 344  
         } else {
 345  0
             return docType;
 346  
         }
 347  
     }
 348  
 
 349  
     public void loadXml(InputStream inputStream, String principalId) {
 350  0
         DocumentTypeXmlParser parser = new DocumentTypeXmlParser();
 351  
         try {
 352  0
             parser.parseDocumentTypes(inputStream);
 353  0
         } catch (Exception e) {
 354  0
             WorkflowServiceErrorException wsee = new WorkflowServiceErrorException("Error parsing documentType XML file", new WorkflowServiceErrorImpl("Error parsing documentType XML file", XML_FILE_PARSE_ERROR));
 355  0
             wsee.initCause(e);
 356  0
             throw wsee;
 357  0
         }
 358  0
     }
 359  
 
 360  
     public Element export(ExportDataSet dataSet) {
 361  0
         DocumentTypeXmlExporter exporter = new DocumentTypeXmlExporter();
 362  0
         return exporter.export(dataSet);
 363  
     }
 364  
     
 365  
     @Override
 366  
         public boolean supportPrettyPrint() {
 367  0
                 return true;
 368  
         }
 369  
 
 370  
     public List getChildDocumentTypes(String documentTypeId) {
 371  0
             List childDocumentTypes = new ArrayList();
 372  0
             List childIds = getDocumentTypeDAO().getChildDocumentTypeIds(documentTypeId);
 373  0
             for (Iterator iter = childIds.iterator(); iter.hasNext();) {
 374  0
                         String childDocumentTypeId = (String) iter.next();
 375  0
                         childDocumentTypes.add(findById(childDocumentTypeId));
 376  0
                 }
 377  0
             return childDocumentTypes;
 378  
     }
 379  
 
 380  
 }