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