Coverage Report - org.kuali.rice.kns.document.DocumentBase
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentBase
0%
0/196
0%
0/48
1.833
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kns.document;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Iterator;
 20  
 import java.util.LinkedHashMap;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 
 24  
 import javax.persistence.CascadeType;
 25  
 import javax.persistence.Column;
 26  
 import javax.persistence.FetchType;
 27  
 import javax.persistence.Id;
 28  
 import javax.persistence.JoinColumn;
 29  
 import javax.persistence.MappedSuperclass;
 30  
 import javax.persistence.OneToMany;
 31  
 import javax.persistence.OneToOne;
 32  
 import javax.persistence.Transient;
 33  
 
 34  
 import org.apache.commons.lang.StringUtils;
 35  
 import org.apache.log4j.Logger;
 36  
 import org.kuali.rice.core.util.type.TypeUtils;
 37  
 import org.kuali.rice.kew.dto.ActionTakenEventDTO;
 38  
 import org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO;
 39  
 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
 40  
 import org.kuali.rice.kew.exception.WorkflowException;
 41  
 import org.kuali.rice.kew.util.KEWConstants;
 42  
 import org.kuali.rice.kim.bo.Person;
 43  
 import org.kuali.rice.kim.service.KIMServiceLocator;
 44  
 import org.kuali.rice.kns.bo.AdHocRoutePerson;
 45  
 import org.kuali.rice.kns.bo.AdHocRouteWorkgroup;
 46  
 import org.kuali.rice.kns.bo.DocumentHeader;
 47  
 import org.kuali.rice.kns.bo.Note;
 48  
 import org.kuali.rice.kns.bo.PersistableBusinessObject;
 49  
 import org.kuali.rice.kns.bo.PersistableBusinessObjectBase;
 50  
 import org.kuali.rice.kns.datadictionary.DocumentEntry;
 51  
 import org.kuali.rice.kns.datadictionary.WorkflowAttributes;
 52  
 import org.kuali.rice.kns.datadictionary.WorkflowProperties;
 53  
 import org.kuali.rice.kns.document.authorization.PessimisticLock;
 54  
 import org.kuali.rice.kns.exception.PessimisticLockingException;
 55  
 import org.kuali.rice.kns.exception.ValidationException;
 56  
 import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
 57  
 import org.kuali.rice.kns.service.AttachmentService;
 58  
 import org.kuali.rice.kns.service.DocumentSerializerService;
 59  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 60  
 import org.kuali.rice.kns.service.KNSServiceLocatorWeb;
 61  
 import org.kuali.rice.kns.service.NoteService;
 62  
 import org.kuali.rice.kns.util.ErrorMessage;
 63  
 import org.kuali.rice.kns.util.GlobalVariables;
 64  
 import org.kuali.rice.kns.util.KNSConstants;
 65  
 import org.kuali.rice.kns.util.KNSPropertyConstants;
 66  
 import org.kuali.rice.kns.util.NoteType;
 67  
 import org.kuali.rice.kns.util.ObjectUtils;
 68  
 import org.kuali.rice.kns.util.documentserializer.AlwaysFalsePropertySerializabilityEvaluator;
 69  
 import org.kuali.rice.kns.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
 70  
 import org.kuali.rice.kns.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator;
 71  
 import org.kuali.rice.kns.util.documentserializer.PropertySerializabilityEvaluator;
 72  
 import org.kuali.rice.kns.workflow.DocumentInitiator;
 73  
 import org.kuali.rice.kns.workflow.KualiDocumentXmlMaterializer;
 74  
 import org.kuali.rice.kns.workflow.KualiTransactionalDocumentInformation;
 75  
 import org.springframework.util.AutoPopulatingList;
 76  
 
 77  
 
 78  
 /**
 79  
  * @see Document
 80  
  */
 81  0
 @MappedSuperclass
 82  
 public abstract class DocumentBase extends PersistableBusinessObjectBase implements Document {
 83  0
     private static final Logger LOG = Logger.getLogger(DocumentBase.class);
 84  
     
 85  
     @Id
 86  
     @Column(name="DOC_HDR_ID")
 87  
     protected String documentNumber;
 88  
     @OneToOne(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE})
 89  
         @JoinColumn(name="DOC_HDR_ID", insertable=false, updatable=false)
 90  
     protected DocumentHeader documentHeader;    
 91  
 
 92  
     @OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE})
 93  
         @JoinColumn(name="DOC_HDR_ID", insertable=false, updatable=false)
 94  
     private List<PessimisticLock> pessimisticLocks;
 95  
 
 96  
     @Transient
 97  
     private List<AdHocRoutePerson> adHocRoutePersons;
 98  
     @Transient
 99  
     private List<AdHocRouteWorkgroup> adHocRouteWorkgroups;
 100  
     @Transient
 101  
     private List<Note> notes;
 102  
     
 103  
     private transient NoteService noteService;
 104  
     private transient AttachmentService attachmentService;
 105  
 
 106  
     /**
 107  
      * Constructs a DocumentBase.java.
 108  
      */
 109  0
     public DocumentBase() {
 110  
         try {
 111  
             // create a new document header object
 112  0
             Class<? extends DocumentHeader> documentHeaderClass = KNSServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderBaseClass();
 113  0
             setDocumentHeader(documentHeaderClass.newInstance());
 114  0
             pessimisticLocks = new ArrayList<PessimisticLock>();
 115  0
             adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
 116  0
             adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
 117  0
             notes = new ArrayList<Note>();
 118  
         }
 119  0
         catch (IllegalAccessException e) {
 120  0
             throw new RuntimeException("Error instantiating DocumentHeader", e);
 121  
         }
 122  0
         catch (InstantiationException e) {
 123  0
             throw new RuntimeException("Error instantiating DocumentHeader", e);
 124  0
         }
 125  0
     }
 126  
 
 127  
     /**
 128  
      * @see org.kuali.rice.kns.document.Document#getAllowsCopy()
 129  
      */
 130  
     public boolean getAllowsCopy() {
 131  
         // TODO Auto-generated method stub
 132  0
         return false;
 133  
     }
 134  
 
 135  
     /**
 136  
      * This is the default document title implementation. It concatenates the document's data dictionary file label attribute and
 137  
      * the document's document header description together. This title is used to populate workflow and will show up in document
 138  
      * search results and user action lists.
 139  
      *
 140  
      * @see org.kuali.rice.kns.document.Document#getDocumentTitle()
 141  
      */
 142  
     public String getDocumentTitle() {
 143  
         try {
 144  0
             String documentTypeLabel = KNSServiceLocatorWeb.getWorkflowInfoService().getDocType(this.getDocumentHeader().getWorkflowDocument().getDocumentType()).getDocTypeLabel();
 145  0
             if (null == documentTypeLabel) {
 146  0
                 documentTypeLabel = "";
 147  
             }
 148  
     
 149  0
             String description = this.getDocumentHeader().getDocumentDescription();
 150  0
             if (null == description) {
 151  0
                 description = "";
 152  
             }
 153  
     
 154  0
             return documentTypeLabel + " - " + description;
 155  
         }
 156  0
         catch (WorkflowException e) {
 157  0
             throw new RuntimeException("Caught Exception getting the document type label", e);
 158  
         }
 159  
     }
 160  
 
 161  
     /**
 162  
      * Uses the persistence service's implementation of OJB's retrieveNonKey() fields method.
 163  
      *
 164  
      * @see org.kuali.rice.kns.bo.BusinessObject#refresh()
 165  
      */
 166  
     @Override
 167  
     public void refresh() {
 168  0
         KNSServiceLocator.getPersistenceService().retrieveNonKeyFields(this);
 169  0
     }
 170  
 
 171  
     /**
 172  
      * Checks to see if the objectId value is empty. If so, it will try to refresh the object from the DB.
 173  
      *
 174  
      * @see org.kuali.rice.kns.document.Document#refreshIfEmpty()
 175  
      */
 176  
     public void refreshIfEmpty() {
 177  0
         if (null == this.getDocumentHeader()) {
 178  0
             this.refresh();
 179  
         }
 180  0
         else if (StringUtils.isEmpty(this.getDocumentHeader().getObjectId())) {
 181  0
             this.refresh();
 182  
         }
 183  0
     }
 184  
 
 185  
     /**
 186  
      * Uses the persistence service to retrieve a reference object of a parent.
 187  
      *
 188  
      * @see org.kuali.rice.kns.document.Document#refreshReferenceObject(java.lang.String)
 189  
      */
 190  
     @Override
 191  
     public void refreshReferenceObject(String referenceObjectName) {
 192  0
         KNSServiceLocator.getPersistenceService().retrieveReferenceObject(this, referenceObjectName);
 193  0
     }
 194  
 
 195  
     /**
 196  
      * @see org.kuali.rice.kns.document.Document#prepareForSave()
 197  
      */
 198  
     public void prepareForSave() {
 199  
         // do nothing
 200  0
     }
 201  
 
 202  
     /**
 203  
      * @see org.kuali.rice.kns.document.Document#processAfterRetrieve()
 204  
      */
 205  
     public void processAfterRetrieve() {
 206  
             // do nothing
 207  0
     }
 208  
 
 209  
     /**
 210  
      * The the default implementation for RouteLevelChange does nothing, but is meant to provide a hook for documents to implement
 211  
      * for other needs.
 212  
      *
 213  
      * @see org.kuali.rice.kns.document.Document#doRouteLevelChange(org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO)
 214  
      */
 215  
     public void doRouteLevelChange(DocumentRouteLevelChangeDTO levelChangeEvent) {
 216  
         // do nothing
 217  0
     }
 218  
     
 219  
     /**
 220  
      * @see org.kuali.rice.kns.document.Document#doActionTaken(org.kuali.rice.kew.dto.ActionTakenEventDTO)
 221  
      */
 222  
     public void doActionTaken(ActionTakenEventDTO event) {
 223  0
         if ( (KNSServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(this.getClass().getName()).getUseWorkflowPessimisticLocking()) && (!getNonLockingActionTakenCodes().contains(event.getActionTaken().getActionTaken())) ) {
 224  
             //DocumentAuthorizer documentAuthorizer = KNSServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(this);
 225  
             //documentAuthorizer.establishWorkflowPessimisticLocking(this);
 226  0
                 KNSServiceLocatorWeb.getPessimisticLockService().establishWorkflowPessimisticLocking(this);
 227  
         }
 228  0
     }
 229  
     
 230  
     protected List<String> getNonLockingActionTakenCodes() {
 231  0
         List<String> actionTakenStatusCodes = new ArrayList<String>();
 232  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_SAVED_CD);
 233  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD);
 234  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_FYI_CD);
 235  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_DENIED_CD);
 236  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_CANCELED_CD);
 237  0
         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_LOG_DOCUMENT_ACTION_CD);
 238  0
         return actionTakenStatusCodes;
 239  
     }
 240  
 
 241  
     /**
 242  
      * The the default implementation for afterWorkflowEngineProcess does nothing, but is meant to provide a hook for
 243  
      * documents to implement for other needs.
 244  
      * 
 245  
      * @see org.kuali.rice.kns.document.Document#afterWorkflowEngineProcess(boolean)
 246  
      */
 247  
     public void afterWorkflowEngineProcess(boolean successfullyProcessed) {
 248  0
         if (KNSServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(this.getClass().getName()).getUseWorkflowPessimisticLocking()) {
 249  0
             if (successfullyProcessed) {
 250  
                 //DocumentAuthorizer documentAuthorizer = KNSServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(this);
 251  
                 //documentAuthorizer.releaseWorkflowPessimisticLocking(this);
 252  0
                     KNSServiceLocatorWeb.getPessimisticLockService().releaseWorkflowPessimisticLocking(this);
 253  
             }
 254  
         }
 255  0
     }
 256  
 
 257  
     /**
 258  
      * The the default implementation for beforeWorkflowEngineProcess does nothing, but is meant to provide a hook for
 259  
      * documents to implement for other needs.
 260  
      * 
 261  
      * @see org.kuali.rice.kns.document.Document#beforeWorkflowEngineProcess()
 262  
      */
 263  
     public void beforeWorkflowEngineProcess() {
 264  
     // do nothing
 265  0
     }
 266  
     
 267  
     
 268  
 
 269  
     /**
 270  
      * The default implementation returns no additional ids for the workflow engine to lock prior to processing.
 271  
      * 
 272  
      * @see org.kuali.rice.kns.document.Document#getWorkflowEngineDocumentIdsToLock()
 273  
      */
 274  
     public List<Long> getWorkflowEngineDocumentIdsToLock() {
 275  0
                 return null;
 276  
         }
 277  
 
 278  
         /**
 279  
      * @see org.kuali.rice.kns.document.Copyable#toCopy()
 280  
      */
 281  
     public void toCopy() throws WorkflowException, IllegalStateException {
 282  0
         if (!this.getAllowsCopy()) {
 283  0
             throw new IllegalStateException(this.getClass().getName() + " does not support document-level copying");
 284  
         }
 285  0
         String sourceDocumentHeaderId = getDocumentNumber();
 286  0
         setNewDocumentHeader();
 287  
                 
 288  0
         getDocumentHeader().setDocumentTemplateNumber(sourceDocumentHeaderId);
 289  
 
 290  0
         addCopyErrorDocumentNote("copied from document " + sourceDocumentHeaderId);
 291  0
     }
 292  
 
 293  
     /**
 294  
      * Gets a new document header for this documents type and sets in the document instance.
 295  
      * 
 296  
      * @throws WorkflowException
 297  
      */
 298  
     protected void setNewDocumentHeader() throws WorkflowException {
 299  0
         TransactionalDocument newDoc = (TransactionalDocument) KNSServiceLocatorWeb.getDocumentService().getNewDocument(getDocumentHeader().getWorkflowDocument().getDocumentType());
 300  0
         newDoc.getDocumentHeader().setDocumentDescription(getDocumentHeader().getDocumentDescription());
 301  0
         newDoc.getDocumentHeader().setOrganizationDocumentNumber(getDocumentHeader().getOrganizationDocumentNumber());
 302  
 
 303  
         try {
 304  0
             ObjectUtils.setObjectPropertyDeep(this, KNSPropertyConstants.DOCUMENT_NUMBER, documentNumber.getClass(), newDoc.getDocumentNumber());
 305  
         }
 306  0
         catch (Exception e) {
 307  0
             LOG.error("Unable to set document number property in copied document " + e.getMessage(),e);
 308  0
             throw new RuntimeException("Unable to set document number property in copied document " + e.getMessage(),e);
 309  0
         }
 310  
 
 311  
         // replace current documentHeader with new documentHeader
 312  0
         setDocumentHeader(newDoc.getDocumentHeader());
 313  0
     }
 314  
 
 315  
     /**
 316  
      * Adds a note to the document indicating it was created by a copy or error correction.
 317  
      * 
 318  
      * @param noteText - text for note
 319  
      */
 320  
     protected void addCopyErrorDocumentNote(String noteText) {
 321  0
         Note note = null;
 322  
         try {
 323  0
             note = KNSServiceLocatorWeb.getDocumentService().createNoteFromDocument(this,noteText);
 324  
         }
 325  0
         catch (Exception e) {
 326  0
          logErrors();
 327  0
          throw new RuntimeException("Couldn't create note on copy or error",e);
 328  0
         }
 329  0
         addNote(note);
 330  0
     }
 331  
 
 332  
     /**
 333  
      * @see org.kuali.rice.kns.document.Document#getXmlForRouteReport()
 334  
      */
 335  
     public String getXmlForRouteReport() {
 336  0
         prepareForSave();
 337  0
         populateDocumentForRouting();
 338  0
         return getDocumentHeader().getWorkflowDocument().getApplicationContent();
 339  
     }
 340  
 
 341  
     /**
 342  
      * @see org.kuali.rice.kns.document.Document#populateDocumentForRouting()
 343  
      */
 344  
     public void populateDocumentForRouting() {
 345  0
         getDocumentHeader().getWorkflowDocument().setApplicationContent(serializeDocumentToXml());
 346  0
     }
 347  
     
 348  
     /**
 349  
      * @see org.kuali.rice.kns.document.Document#serializeDocumentToXml()
 350  
      */
 351  
     public String serializeDocumentToXml() {
 352  0
         DocumentSerializerService documentSerializerService = KNSServiceLocatorWeb.getDocumentSerializerService();
 353  0
         String xml = documentSerializerService.serializeDocumentToXmlForRouting(this);
 354  0
         return xml;
 355  
     }
 356  
 
 357  
     /**
 358  
      * Wraps a document in an instance of KualiDocumentXmlMaterializer, that provides additional metadata for serialization
 359  
      * 
 360  
      * @see org.kuali.rice.kns.document.Document#wrapDocumentWithMetadataForXmlSerialization()
 361  
      */
 362  
     public KualiDocumentXmlMaterializer wrapDocumentWithMetadataForXmlSerialization() {
 363  0
         KualiTransactionalDocumentInformation transInfo = new KualiTransactionalDocumentInformation();
 364  0
         DocumentInitiator initiator = new DocumentInitiator();
 365  0
         String initiatorPrincipalId = getDocumentHeader().getWorkflowDocument().getRouteHeader().getInitiatorPrincipalId();
 366  0
         Person initiatorUser = KIMServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
 367  0
         initiator.setPerson(initiatorUser);
 368  0
         transInfo.setDocumentInitiator(initiator);
 369  0
         KualiDocumentXmlMaterializer xmlWrapper = new KualiDocumentXmlMaterializer();
 370  0
         xmlWrapper.setDocument(this);
 371  0
         xmlWrapper.setKualiTransactionalDocumentInformation(transInfo);
 372  0
         return xmlWrapper;
 373  
     }
 374  
 
 375  
     /**
 376  
      * If workflowProperties have been defined within the data dictionary for this document, then it returns an instance of 
 377  
      * {@link BusinessObjectPropertySerializibilityEvaluator} initialized with the properties.  If none have been defined, then returns 
 378  
      * {@link AlwaysTruePropertySerializibilityEvaluator}.
 379  
      * 
 380  
      * @see org.kuali.rice.kns.document.Document#getDocumentPropertySerizabilityEvaluator()
 381  
      */
 382  
     public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
 383  0
         String docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentType();
 384  0
         DocumentEntry documentEntry = KNSServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(docTypeName);
 385  0
         WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties();
 386  0
         WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
 387  0
         return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes);
 388  
     }
 389  
     
 390  
     protected PropertySerializabilityEvaluator createPropertySerializabilityEvaluator(WorkflowProperties workflowProperties, WorkflowAttributes workflowAttributes) {
 391  0
             if (workflowAttributes != null) {
 392  0
                     return new AlwaysFalsePropertySerializabilityEvaluator();
 393  
             }
 394  0
             if (workflowProperties == null) {
 395  0
                     return new AlwaysTruePropertySerializibilityEvaluator();
 396  
             }
 397  0
             PropertySerializabilityEvaluator evaluator = new BusinessObjectPropertySerializibilityEvaluator();
 398  0
             evaluator.initializeEvaluator(this);
 399  0
             return evaluator;
 400  
     }
 401  
     
 402  
     /**
 403  
      * Returns the POJO property name of "this" document in the object returned by {@link #wrapDocumentWithMetadataForXmlSerialization()}
 404  
      * 
 405  
      * @see org.kuali.rice.kns.document.Document#getBasePathToDocumentDuringSerialization()
 406  
      */
 407  
     public String getBasePathToDocumentDuringSerialization() {
 408  0
         return "document";
 409  
     }
 410  
     
 411  
     
 412  
     /**
 413  
      * @see org.kuali.rice.kns.document.Document#getDocumentHeader()
 414  
      */
 415  
     public DocumentHeader getDocumentHeader() {
 416  0
         return this.documentHeader;
 417  
     }
 418  
 
 419  
     /**
 420  
      * @see org.kuali.rice.kns.document.Document#setDocumentHeader(org.kuali.rice.kns.document.DocumentHeader)
 421  
      */
 422  
     public void setDocumentHeader(DocumentHeader documentHeader) {
 423  0
         this.documentHeader = documentHeader;
 424  0
     }
 425  
 
 426  
     /**
 427  
      * @see org.kuali.rice.kns.document.Document#getDocumentNumber()
 428  
      */
 429  
     public String getDocumentNumber() {
 430  0
         return documentNumber;
 431  
     }
 432  
 
 433  
     /**
 434  
      * @see org.kuali.rice.kns.document.Document#setDocumentNumber(java.lang.String)
 435  
      */
 436  
     public void setDocumentNumber(String documentNumber) {
 437  0
         this.documentNumber = documentNumber;
 438  0
     }
 439  
 
 440  
     /**
 441  
      * @see org.kuali.rice.kns.document.Document#getAdHocRoutePersons()
 442  
      */
 443  
     public List<AdHocRoutePerson> getAdHocRoutePersons() {
 444  0
         return adHocRoutePersons;
 445  
     }
 446  
 
 447  
     /**
 448  
      * @see org.kuali.rice.kns.document.Document#setAdHocRoutePersons(java.util.List)
 449  
      */
 450  
     public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRoutePersons) {
 451  0
         this.adHocRoutePersons = adHocRoutePersons;
 452  0
 }
 453  
     /**
 454  
      * @see org.kuali.rice.kns.document.Document#getAdHocRouteWorkgroups()
 455  
      */
 456  
     public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() {
 457  0
         return adHocRouteWorkgroups;
 458  
     }
 459  
 
 460  
     /**
 461  
      * @see org.kuali.rice.kns.document.Document#setAdHocRouteWorkgroups(java.util.List)
 462  
      */
 463  
     public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) {
 464  0
         this.adHocRouteWorkgroups = adHocRouteWorkgroups;
 465  0
     }
 466  
 
 467  
     public void postProcessSave(KualiDocumentEvent event) {
 468  
         // TODO Auto-generated method stub
 469  
 
 470  0
         }
 471  
 
 472  
     /**
 473  
      * Override this method with implementation specific prepareForSave logic
 474  
      * 
 475  
      * @see org.kuali.rice.kns.document.Document#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent)
 476  
      */
 477  
     public void prepareForSave(KualiDocumentEvent event) {
 478  
             // do nothing by default
 479  0
     }
 480  
 
 481  
     public void validateBusinessRules(KualiDocumentEvent event) {
 482  0
         if (GlobalVariables.getMessageMap().hasErrors()) {
 483  0
             logErrors();
 484  0
             throw new ValidationException("errors occured before business rule");
 485  
         }
 486  
 
 487  
         // perform validation against rules engine
 488  0
         LOG.info("invoking rules engine on document " + getDocumentNumber());
 489  0
         boolean isValid = true;
 490  0
         isValid = KNSServiceLocatorWeb.getKualiRuleService().applyRules(event);
 491  
 
 492  
         // check to see if the br eval passed or failed
 493  0
         if (!isValid) {
 494  0
             logErrors();
 495  
             // TODO: better error handling at the lower level and a better error message are
 496  
             // needed here
 497  0
             throw new ValidationException("business rule evaluation failed");
 498  
         }
 499  0
         else if (GlobalVariables.getMessageMap().hasErrors()) {
 500  0
             logErrors();
 501  0
             throw new ValidationException("Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
 502  
         }
 503  0
         LOG.debug("validation completed");
 504  
 
 505  0
     }
 506  
 
 507  
     /**
 508  
      * This method logs errors.
 509  
      */
 510  
     protected void logErrors() {
 511  0
             if ( LOG.isInfoEnabled() ) {
 512  0
                 if (GlobalVariables.getMessageMap().hasErrors()) {
 513  
         
 514  0
                     for (Iterator<Map.Entry<String, AutoPopulatingList<ErrorMessage>>> i = GlobalVariables.getMessageMap().getAllPropertiesAndErrors().iterator(); i.hasNext();) {
 515  0
                         Map.Entry<String, AutoPopulatingList<ErrorMessage>> e = i.next();
 516  
         
 517  0
                         StringBuffer logMessage = new StringBuffer();
 518  0
                         logMessage.append("[" + e.getKey() + "] ");
 519  0
                         boolean first = true;
 520  
         
 521  0
                         AutoPopulatingList<ErrorMessage> errorList = e.getValue();
 522  0
                         for (Iterator<ErrorMessage> j = errorList.iterator(); j.hasNext();) {
 523  0
                             ErrorMessage em = j.next();
 524  
         
 525  0
                             if (first) {
 526  0
                                 first = false;
 527  
                             }
 528  
                             else {
 529  0
                                 logMessage.append(";");
 530  
                             }
 531  0
                             logMessage.append(em);
 532  0
                         }
 533  
         
 534  0
                         LOG.info(logMessage);
 535  0
                     }
 536  
                 }
 537  
             }
 538  0
     }
 539  
 
 540  
     /**
 541  
      * Hook for override
 542  
      * 
 543  
      * @see org.kuali.rice.kns.document.Document#generateSaveEvents()
 544  
      */
 545  
     public List<KualiDocumentEvent> generateSaveEvents() {
 546  0
         return new ArrayList<KualiDocumentEvent>();
 547  
     }
 548  
 
 549  
     /**
 550  
      * @see org.kuali.rice.kns.document.Document#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO)
 551  
      */
 552  
     public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
 553  
         // do nothing
 554  0
     }
 555  
     
 556  
     /**
 557  
      * Returns the business object with which notes related to this document should be associated.
 558  
      * By default, the {@link DocumentHeader} of this document will be returned as the note target.
 559  
      * 
 560  
      * <p>Sub classes can override this method if they want notes to be associated with something
 561  
      * other than the document header.  If this method is overridden, the {@link #getNoteType()}
 562  
      * method should be overridden to return {@link NoteType#BUSINESS_OBJECT}
 563  
      * 
 564  
      * @return Returns the documentBusinessObject.
 565  
      */
 566  
     @Override
 567  
     public PersistableBusinessObject getNoteTarget() {
 568  0
         return getDocumentHeader();
 569  
     }
 570  
     
 571  
     /**
 572  
          * Returns the {@link NoteType} to use for notes associated with this document.
 573  
          * By default this returns {@link NoteType#DOCUMENT_HEADER} since notes are
 574  
          * associated with the {@link DocumentHeader} record by default.
 575  
          * 
 576  
          * <p>The case in which this should be overridden is if {@link #getNoteTarget()} is
 577  
          * overridden to return an object other than the DocumentHeader.
 578  
          *
 579  
          * @return the note type to use for notes associated with this document
 580  
          * 
 581  
          * @see org.kuali.rice.kns.document.Document#getNoteType()
 582  
          */
 583  
         @Override
 584  
         public NoteType getNoteType() {
 585  0
                 return NoteType.DOCUMENT_HEADER;
 586  
         }
 587  
 
 588  
         /**
 589  
          * @see org.kuali.rice.kns.document.Document#addNote(org.kuali.rice.kns.bo.Note)
 590  
          */
 591  
     @Override
 592  
         public void addNote(Note note) {
 593  0
             if (note == null) {
 594  0
                     throw new IllegalArgumentException("Note cannot be null.");
 595  
             }
 596  0
                 notes.add(note);
 597  0
         }
 598  
 
 599  
     /**
 600  
      * @see org.kuali.rice.kns.document.Document#removeNote(org.kuali.rice.kns.bo.Note)
 601  
      */
 602  
         @Override
 603  
         public boolean removeNote(Note note) {
 604  0
                 if (note == null) {
 605  0
                     throw new IllegalArgumentException("Note cannot be null.");
 606  
             }
 607  0
                 return notes.remove(note);
 608  
         }
 609  
 
 610  
         /**
 611  
          * @see org.kuali.rice.kns.document.Document#getNote(int)
 612  
          */
 613  
         @Override
 614  
         public Note getNote(int index) {
 615  0
                 return notes.get(index);
 616  
         }
 617  
 
 618  
         /**
 619  
          * @see org.kuali.rice.kns.document.Document#getNotes()
 620  
          */
 621  
         @Override
 622  
         public List<Note> getNotes() {
 623  0
                 return notes;
 624  
         }
 625  
         
 626  
         /**
 627  
          * @see org.kuali.rice.kns.document.Document#setNotes(java.util.List)
 628  
          */
 629  
         @Override
 630  
         public void setNotes(List<Note> notes) {
 631  0
                 if (notes == null) {
 632  0
                         throw new IllegalArgumentException("List of notes must be non-null.");
 633  
                 }
 634  0
                 this.notes = notes;
 635  0
         }
 636  
 
 637  
     /**
 638  
      * @see org.kuali.rice.kns.document.Document#getPessimisticLocks()
 639  
      */
 640  
     public List<PessimisticLock> getPessimisticLocks() {
 641  0
         return this.pessimisticLocks;
 642  
     }
 643  
     
 644  
     /**
 645  
      * @see org.kuali.rice.kns.document.Document#refreshPessimisticLocks()
 646  
      * @deprecated
 647  
      * This is not needed with the relationship set up with JPA annotations
 648  
      */
 649  
     @Deprecated 
 650  
     public void refreshPessimisticLocks() {
 651  0
         this.pessimisticLocks.clear();
 652  0
         this.pessimisticLocks = KNSServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForDocument(this.documentNumber);
 653  0
     }
 654  
 
 655  
     /**
 656  
      * @param pessimisticLocks the PessimisticLock objects to set
 657  
      */
 658  
     public void setPessimisticLocks(List<PessimisticLock> pessimisticLocks) {
 659  0
         this.pessimisticLocks = pessimisticLocks;
 660  0
     }
 661  
     
 662  
     /**
 663  
      * @see org.kuali.rice.kns.document.Document#addPessimisticLock(org.kuali.rice.kns.document.authorization.PessimisticLock)
 664  
      */
 665  
     public void addPessimisticLock(PessimisticLock lock) {
 666  0
         this.pessimisticLocks.add(lock);
 667  0
     }
 668  
     
 669  
     /**
 670  
      * @see org.kuali.rice.kns.document.Document#getLockClearningMethodNames()
 671  
      */
 672  
     public List<String> getLockClearningMethodNames() {
 673  0
         List<String> methodToCalls = new ArrayList<String>();
 674  0
         methodToCalls.add(KNSConstants.CLOSE_METHOD);
 675  0
         methodToCalls.add(KNSConstants.CANCEL_METHOD);
 676  
 //        methodToCalls.add(RiceConstants.BLANKET_APPROVE_METHOD);
 677  0
         methodToCalls.add(KNSConstants.ROUTE_METHOD);
 678  0
         methodToCalls.add(KNSConstants.APPROVE_METHOD);
 679  0
         methodToCalls.add(KNSConstants.DISAPPROVE_METHOD);
 680  0
         return methodToCalls;
 681  
     }
 682  
 
 683  
     /**
 684  
      * This default implementation simply returns false to indicate that custom lock descriptors are not supported by DocumentBase. If custom lock
 685  
      * descriptors are needed, the appropriate subclasses should override this method.
 686  
      * 
 687  
      * @see org.kuali.rice.kns.document.Document#useCustomLockDescriptors()
 688  
      */
 689  
     public boolean useCustomLockDescriptors() {
 690  0
             return false;
 691  
     }
 692  
 
 693  
     /**
 694  
      * This default implementation just throws a PessimisticLockingException. Subclasses of DocumentBase that need support for custom lock descriptors
 695  
      * should override this method.
 696  
      * 
 697  
      * @see org.kuali.rice.kns.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.bo.Person)
 698  
      */
 699  
     public String getCustomLockDescriptor(Person user) {
 700  0
             throw new PessimisticLockingException("Document " + getDocumentNumber() +
 701  
                             " is using pessimistic locking with custom lock descriptors, but the document class has not overriden the getCustomLockDescriptor method");
 702  
     }
 703  
     
 704  
     protected AttachmentService getAttachmentService() {
 705  0
                 if ( attachmentService == null ) {
 706  0
                         attachmentService = KNSServiceLocator.getAttachmentService();
 707  
                 }
 708  0
                 return attachmentService;
 709  
         }
 710  
     
 711  
     protected NoteService getNoteService() {
 712  0
                 if ( noteService == null ) {
 713  0
                         noteService = KNSServiceLocator.getNoteService();
 714  
                 }
 715  0
                 return noteService;
 716  
         }
 717  
 }