View Javadoc

1   /**
2    * Copyright 2005-2012 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.krad.maintenance;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
20  import org.kuali.rice.core.api.CoreApiServiceLocator;
21  import org.kuali.rice.core.api.encryption.EncryptionService;
22  import org.kuali.rice.kim.api.identity.Person;
23  import org.kuali.rice.krad.bo.BusinessObject;
24  import org.kuali.rice.krad.bo.DocumentHeader;
25  import org.kuali.rice.krad.bo.Note;
26  import org.kuali.rice.krad.bo.PersistableBusinessObject;
27  import org.kuali.rice.krad.exception.PessimisticLockingException;
28  import org.kuali.rice.krad.service.BusinessObjectService;
29  import org.kuali.rice.krad.service.DataObjectAuthorizationService;
30  import org.kuali.rice.krad.service.DataObjectMetaDataService;
31  import org.kuali.rice.krad.service.DocumentDictionaryService;
32  import org.kuali.rice.krad.service.KRADServiceLocator;
33  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
34  import org.kuali.rice.krad.service.LookupService;
35  import org.kuali.rice.krad.service.MaintenanceDocumentService;
36  import org.kuali.rice.krad.uif.container.CollectionGroup;
37  import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
38  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
39  import org.kuali.rice.krad.uif.view.View;
40  import org.kuali.rice.krad.util.KRADConstants;
41  import org.kuali.rice.krad.util.ObjectUtils;
42  import org.kuali.rice.krad.web.form.MaintenanceForm;
43  
44  import java.security.GeneralSecurityException;
45  import java.util.ArrayList;
46  import java.util.Collection;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * Default implementation of the <code>Maintainable</code> interface
53   *
54   * @author Kuali Rice Team (rice.collab@kuali.org)
55   */
56  public class MaintainableImpl extends ViewHelperServiceImpl implements Maintainable {
57      private static final long serialVersionUID = 9125271369161634992L;
58  
59      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintainableImpl.class);
60  
61      private String documentNumber;
62      private Object dataObject;
63      private Class<?> dataObjectClass;
64      private String maintenanceAction;
65  
66      private transient LookupService lookupService;
67      private transient DataObjectAuthorizationService dataObjectAuthorizationService;
68      private transient DataObjectMetaDataService dataObjectMetaDataService;
69      private transient DocumentDictionaryService documentDictionaryService;
70      private transient EncryptionService encryptionService;
71      private transient BusinessObjectService businessObjectService;
72      private transient MaintenanceDocumentService maintenanceDocumentService;
73  
74      /**
75       * @see org.kuali.rice.krad.maintenance.Maintainable#retrieveObjectForEditOrCopy(MaintenanceDocument, java.util.Map)
76       */
77      @Override
78      public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
79          Object dataObject = null;
80  
81          try {
82              dataObject = getLookupService().findObjectBySearch(getDataObjectClass(), dataObjectKeys);
83          } catch (ClassNotPersistenceCapableException ex) {
84              if (!document.getOldMaintainableObject().isExternalBusinessObject()) {
85                  throw new RuntimeException("Data Object Class: "
86                          + getDataObjectClass()
87                          + " is not persistable and is not externalizable - configuration error");
88              }
89              // otherwise, let fall through
90          }
91  
92          return dataObject;
93      }
94  
95      /**
96       * @see org.kuali.rice.krad.maintenance.Maintainable#setDocumentNumber
97       */
98      @Override
99      public void setDocumentNumber(String documentNumber) {
100         this.documentNumber = documentNumber;
101     }
102 
103     /**
104      * @see org.kuali.rice.krad.maintenance.Maintainable#getDocumentTitle
105      */
106     @Override
107     public String getDocumentTitle(MaintenanceDocument document) {
108         // default implementation is to allow MaintenanceDocumentBase to
109         // generate the doc title
110         return "";
111     }
112 
113     /**
114      * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObject
115      */
116     @Override
117     public Object getDataObject() {
118         return dataObject;
119     }
120 
121     /**
122      * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObject
123      */
124     @Override
125     public void setDataObject(Object object) {
126         this.dataObject = object;
127     }
128 
129     /**
130      * @see org.kuali.rice.krad.maintenance.Maintainable#getDataObjectClass
131      */
132     @Override
133     public Class getDataObjectClass() {
134         return dataObjectClass;
135     }
136 
137     /**
138      * @see org.kuali.rice.krad.maintenance.Maintainable#setDataObjectClass
139      */
140     @Override
141     public void setDataObjectClass(Class dataObjectClass) {
142         this.dataObjectClass = dataObjectClass;
143     }
144 
145     /**
146      * @see org.kuali.rice.krad.maintenance.Maintainable#getMaintenanceAction
147      */
148     @Override
149     public String getMaintenanceAction() {
150         return maintenanceAction;
151     }
152 
153     /**
154      * @see org.kuali.rice.krad.maintenance.Maintainable#setMaintenanceAction
155      */
156     @Override
157     public void setMaintenanceAction(String maintenanceAction) {
158         this.maintenanceAction = maintenanceAction;
159     }
160 
161     /**
162      * Note: as currently implemented, every key field for a given
163      * data object class must have a visible getter
164      *
165      * @see org.kuali.rice.krad.maintenance.Maintainable#generateMaintenanceLocks
166      */
167     @Override
168     public List<MaintenanceLock> generateMaintenanceLocks() {
169         List<MaintenanceLock> maintenanceLocks = new ArrayList<MaintenanceLock>();
170         StringBuffer lockRepresentation = new StringBuffer(dataObjectClass.getName());
171         lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_CLASS_DELIM);
172 
173         Object bo = getDataObject();
174         List keyFieldNames = getDocumentDictionaryService().getLockingKeys(getDocumentTypeName());
175 
176         for (Iterator i = keyFieldNames.iterator(); i.hasNext(); ) {
177             String fieldName = (String) i.next();
178             Object fieldValue = ObjectUtils.getPropertyValue(bo, fieldName);
179             if (fieldValue == null) {
180                 fieldValue = "";
181             }
182 
183             // check if field is a secure
184             if (getDataObjectAuthorizationService()
185                     .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObjectClass, fieldName)) {
186                 try {
187                     fieldValue = getEncryptionService().encrypt(fieldValue);
188                 } catch (GeneralSecurityException e) {
189                     LOG.error("Unable to encrypt secure field for locking representation " + e.getMessage());
190                     throw new RuntimeException(
191                             "Unable to encrypt secure field for locking representation " + e.getMessage());
192                 }
193             }
194 
195             lockRepresentation.append(fieldName);
196             lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_FIELDNAME_DELIM);
197             lockRepresentation.append(String.valueOf(fieldValue));
198             if (i.hasNext()) {
199                 lockRepresentation.append(KRADConstants.Maintenance.LOCK_AFTER_VALUE_DELIM);
200             }
201         }
202 
203         MaintenanceLock maintenanceLock = new MaintenanceLock();
204         maintenanceLock.setDocumentNumber(documentNumber);
205         maintenanceLock.setLockingRepresentation(lockRepresentation.toString());
206         maintenanceLocks.add(maintenanceLock);
207 
208         return maintenanceLocks;
209     }
210 
211     /**
212      * Retrieves the document type name from the data dictionary based on
213      * business object class
214      */
215     protected String getDocumentTypeName() {
216         return getDocumentDictionaryService().getMaintenanceDocumentTypeName(dataObjectClass);
217     }
218 
219     /**
220      * @see org.kuali.rice.krad.maintenance.Maintainable#saveDataObject
221      */
222     @Override
223     public void saveDataObject() {
224         if (dataObject instanceof PersistableBusinessObject) {
225             if(getMaintenanceAction().equals(KRADConstants.MAINTENANCE_COPY_ACTION)){
226                if(org.apache.commons.lang.StringUtils.isNotBlank(((PersistableBusinessObject) dataObject).getObjectId())){
227                     ((PersistableBusinessObject) dataObject).setObjectId("");
228                 }
229                  if(null!= ((PersistableBusinessObject) dataObject).getVersionNumber()){
230                     ((PersistableBusinessObject) dataObject).setVersionNumber(null);
231                 }
232             }
233             getBusinessObjectService().linkAndSave((PersistableBusinessObject) dataObject);
234         } else {
235             throw new RuntimeException(
236                     "Cannot save object of type: " + dataObjectClass + " with business object service");
237         }
238     }
239 
240     /**
241      * @see org.kuali.rice.krad.maintenance.Maintainable#deleteDataObject
242      */
243     @Override
244     public void deleteDataObject() {
245         if (dataObject == null) {
246             return;
247         }
248 
249         if (dataObject instanceof PersistableBusinessObject) {
250             getBusinessObjectService().delete((PersistableBusinessObject) dataObject);
251             dataObject = null;
252         } else {
253             throw new RuntimeException(
254                     "Cannot delete object of type: " + dataObjectClass + " with business object service");
255         }
256     }
257 
258     /**
259      * @see org.kuali.rice.krad.maintenance.Maintainable#doRouteStatusChange
260      */
261     @Override
262     public void doRouteStatusChange(DocumentHeader documentHeader) {
263         // no default implementation
264     }
265 
266     /**
267      * @see org.kuali.rice.krad.maintenance.Maintainable#getLockingDocumentId
268      */
269     @Override
270     public String getLockingDocumentId() {
271         return getMaintenanceDocumentService().getLockingDocumentId(this, documentNumber);
272     }
273 
274     /**
275      * @see org.kuali.rice.krad.maintenance.Maintainable#getWorkflowEngineDocumentIdsToLock
276      */
277     @Override
278     public List<String> getWorkflowEngineDocumentIdsToLock() {
279         return null;
280     }
281 
282     /**
283      * Default implementation simply returns false to indicate that custom
284      * lock descriptors are not supported by MaintainableImpl. If custom
285      * lock descriptors are needed, the appropriate subclasses should override
286      * this method
287      *
288      * @see org.kuali.rice.krad.maintenance.Maintainable#useCustomLockDescriptors
289      */
290     @Override
291     public boolean useCustomLockDescriptors() {
292         return false;
293     }
294 
295     /**
296      * Default implementation just throws a PessimisticLockingException.
297      * Subclasses of MaintainableImpl that need support for custom lock
298      * descriptors should override this method
299      *
300      * @see org.kuali.rice.krad.maintenance.Maintainable#getCustomLockDescriptor
301      */
302     @Override
303     public String getCustomLockDescriptor(Person user) {
304         throw new PessimisticLockingException("The Maintainable for document " + documentNumber +
305                 " is using pessimistic locking with custom lock descriptors, but the Maintainable has not overridden the getCustomLockDescriptor method");
306     }
307 
308     /**
309      * @see org.kuali.rice.krad.maintenance.Maintainable#isNotesEnabled
310      */
311     @Override
312     public boolean isNotesEnabled() {
313         return getDataObjectMetaDataService().areNotesSupported(dataObjectClass);
314     }
315 
316     /**
317      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isExternalBusinessObject
318      */
319     @Override
320     public boolean isExternalBusinessObject() {
321         return false;
322     }
323 
324     /**
325      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#prepareExternalBusinessObject
326      */
327     @Override
328     public void prepareExternalBusinessObject(BusinessObject businessObject) {
329         // by default do nothing
330     }
331 
332     /**
333      * Checks whether the data object is not null and has its primary key values populated
334      *
335      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isOldDataObjectInDocument
336      */
337     @Override
338     public boolean isOldDataObjectInDocument() {
339         boolean isOldDataObjectInExistence = true;
340 
341         if (getDataObject() == null) {
342             isOldDataObjectInExistence = false;
343         } else {
344             Map<String, ?> keyFieldValues = getDataObjectMetaDataService().getPrimaryKeyFieldValues(getDataObject());
345             for (Object keyValue : keyFieldValues.values()) {
346                 if (keyValue == null) {
347                     isOldDataObjectInExistence = false;
348                 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
349                     isOldDataObjectInExistence = false;
350                 }
351 
352                 if (!isOldDataObjectInExistence) {
353                     break;
354                 }
355             }
356         }
357 
358         return isOldDataObjectInExistence;
359     }
360 
361     /**
362      * @see org.kuali.rice.krad.maintenance.Maintainable#prepareForSave
363      */
364     @Override
365     public void prepareForSave() {
366         // by default do nothing
367     }
368 
369     /**
370      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterRetrieve
371      */
372     @Override
373     public void processAfterRetrieve() {
374         // by default do nothing
375     }
376 
377     /**
378      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#setupNewFromExisting
379      */
380     @Override
381     public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
382         // by default do nothing
383     }
384 
385     /**
386      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterCopy
387      */
388     @Override
389     public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> requestParameters) {
390         // by default do nothing
391     }
392 
393     /**
394      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterEdit
395      */
396     @Override
397     public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
398         // by default do nothing
399     }
400 
401     /**
402      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterNew
403      */
404     @Override
405     public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
406         // by default do nothing
407     }
408 
409     /**
410      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterPost
411      */
412     @Override
413     public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) {
414         // by default do nothing
415     }
416 
417     /**
418      * In the case of edit maintenance adds a new blank line to the old side
419      *
420      * TODO: should this write some sort of missing message on the old side
421      * instead?
422      *
423      * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View,
424      *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,
425      *      java.lang.Object)
426      */
427     @Override
428     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
429         super.processAfterAddLine(view, collectionGroup, model, addLine);
430         
431         // Check for maintenance documents in edit but exclude notes
432         if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction()) && !(addLine instanceof Note)) {
433             MaintenanceForm maintenanceForm = (MaintenanceForm) model;
434             MaintenanceDocument document = maintenanceForm.getDocument();
435 
436             // get the old object's collection
437             Collection<Object> oldCollection = ObjectPropertyUtils
438                     .getPropertyValue(document.getOldMaintainableObject().getDataObject(),
439                             collectionGroup.getPropertyName());
440             try {
441                 Object blankLine = collectionGroup.getCollectionObjectClass().newInstance();
442                 oldCollection.add(blankLine);
443             } catch (Exception e) {
444                 throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
445             }
446         }
447     }
448     
449     /**
450      * In the case of edit maintenance deleted the item on the old side
451      *
452      *
453      * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(View,
454      *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,  int)
455      */
456     @Override
457     protected void processAfterDeleteLine(View view, CollectionGroup collectionGroup, Object model, int lineIndex) {
458         super.processAfterDeleteLine(view, collectionGroup, model, lineIndex);
459         
460         // Check for maintenance documents in edit but exclude notes
461         if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction()) 
462                 && !collectionGroup.getCollectionObjectClass().getName().equals(Note.class.getName())) {
463             MaintenanceForm maintenanceForm = (MaintenanceForm) model;
464             MaintenanceDocument document = maintenanceForm.getDocument();
465 
466             // get the old object's collection
467             Collection<Object> oldCollection = ObjectPropertyUtils
468                     .getPropertyValue(document.getOldMaintainableObject().getDataObject(),
469                             collectionGroup.getPropertyName());
470             try {
471                 // Remove the object at lineIndex from the collection
472                 oldCollection.remove(oldCollection.toArray()[lineIndex]);
473             } catch (Exception e) {
474                 throw new RuntimeException("Unable to delete line instance for old maintenance object", e);
475             }
476         }
477     }    
478 
479     /**
480      * Retrieves the document number configured on this maintainable
481      *
482      * @return String document number
483      */
484     protected String getDocumentNumber() {
485         return this.documentNumber;
486     }
487 
488     protected LookupService getLookupService() {
489         if (lookupService == null) {
490             lookupService = KRADServiceLocatorWeb.getLookupService();
491         }
492         return this.lookupService;
493     }
494 
495     public void setLookupService(LookupService lookupService) {
496         this.lookupService = lookupService;
497     }
498 
499     protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
500         if (dataObjectAuthorizationService == null) {
501             this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService();
502         }
503         return dataObjectAuthorizationService;
504     }
505 
506     public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
507         this.dataObjectAuthorizationService = dataObjectAuthorizationService;
508     }
509 
510     protected DataObjectMetaDataService getDataObjectMetaDataService() {
511         if (dataObjectMetaDataService == null) {
512             this.dataObjectMetaDataService = KRADServiceLocatorWeb.getDataObjectMetaDataService();
513         }
514         return dataObjectMetaDataService;
515     }
516 
517     public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) {
518         this.dataObjectMetaDataService = dataObjectMetaDataService;
519     }
520 
521     public DocumentDictionaryService getDocumentDictionaryService() {
522         if (documentDictionaryService == null) {
523             this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
524         }
525         return documentDictionaryService;
526     }
527 
528     public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
529         this.documentDictionaryService = documentDictionaryService;
530     }
531 
532     protected EncryptionService getEncryptionService() {
533         if (encryptionService == null) {
534             encryptionService = CoreApiServiceLocator.getEncryptionService();
535         }
536         return encryptionService;
537     }
538 
539     public void setEncryptionService(EncryptionService encryptionService) {
540         this.encryptionService = encryptionService;
541     }
542 
543     protected BusinessObjectService getBusinessObjectService() {
544         if (businessObjectService == null) {
545             businessObjectService = KRADServiceLocator.getBusinessObjectService();
546         }
547         return businessObjectService;
548     }
549 
550     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
551         this.businessObjectService = businessObjectService;
552     }
553 
554     protected MaintenanceDocumentService getMaintenanceDocumentService() {
555         if (maintenanceDocumentService == null) {
556             maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService();
557         }
558         return maintenanceDocumentService;
559     }
560 
561     public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) {
562         this.maintenanceDocumentService = maintenanceDocumentService;
563     }
564 }