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             getBusinessObjectService().linkAndSave((PersistableBusinessObject) dataObject);
226         } else {
227             throw new RuntimeException(
228                     "Cannot save object of type: " + dataObjectClass + " with business object service");
229         }
230     }
231 
232     /**
233      * @see org.kuali.rice.krad.maintenance.Maintainable#deleteDataObject
234      */
235     @Override
236     public void deleteDataObject() {
237         if (dataObject == null) {
238             return;
239         }
240 
241         if (dataObject instanceof PersistableBusinessObject) {
242             getBusinessObjectService().delete((PersistableBusinessObject) dataObject);
243             dataObject = null;
244         } else {
245             throw new RuntimeException(
246                     "Cannot delete object of type: " + dataObjectClass + " with business object service");
247         }
248     }
249 
250     /**
251      * @see org.kuali.rice.krad.maintenance.Maintainable#doRouteStatusChange
252      */
253     @Override
254     public void doRouteStatusChange(DocumentHeader documentHeader) {
255         // no default implementation
256     }
257 
258     /**
259      * @see org.kuali.rice.krad.maintenance.Maintainable#getLockingDocumentId
260      */
261     @Override
262     public String getLockingDocumentId() {
263         return getMaintenanceDocumentService().getLockingDocumentId(this, documentNumber);
264     }
265 
266     /**
267      * @see org.kuali.rice.krad.maintenance.Maintainable#getWorkflowEngineDocumentIdsToLock
268      */
269     @Override
270     public List<String> getWorkflowEngineDocumentIdsToLock() {
271         return null;
272     }
273 
274     /**
275      * Default implementation simply returns false to indicate that custom
276      * lock descriptors are not supported by MaintainableImpl. If custom
277      * lock descriptors are needed, the appropriate subclasses should override
278      * this method
279      *
280      * @see org.kuali.rice.krad.maintenance.Maintainable#useCustomLockDescriptors
281      */
282     @Override
283     public boolean useCustomLockDescriptors() {
284         return false;
285     }
286 
287     /**
288      * Default implementation just throws a PessimisticLockingException.
289      * Subclasses of MaintainableImpl that need support for custom lock
290      * descriptors should override this method
291      *
292      * @see org.kuali.rice.krad.maintenance.Maintainable#getCustomLockDescriptor
293      */
294     @Override
295     public String getCustomLockDescriptor(Person user) {
296         throw new PessimisticLockingException("The Maintainable for document " + documentNumber +
297                 " is using pessimistic locking with custom lock descriptors, but the Maintainable has not overridden the getCustomLockDescriptor method");
298     }
299 
300     /**
301      * @see org.kuali.rice.krad.maintenance.Maintainable#isNotesEnabled
302      */
303     @Override
304     public boolean isNotesEnabled() {
305         return getDataObjectMetaDataService().areNotesSupported(dataObjectClass);
306     }
307 
308     /**
309      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isExternalBusinessObject
310      */
311     @Override
312     public boolean isExternalBusinessObject() {
313         return false;
314     }
315 
316     /**
317      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#prepareExternalBusinessObject
318      */
319     @Override
320     public void prepareExternalBusinessObject(BusinessObject businessObject) {
321         // by default do nothing
322     }
323 
324     /**
325      * Checks whether the data object is not null and has its primary key values populated
326      *
327      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#isOldDataObjectInDocument
328      */
329     @Override
330     public boolean isOldDataObjectInDocument() {
331         boolean isOldDataObjectInExistence = true;
332 
333         if (getDataObject() == null) {
334             isOldDataObjectInExistence = false;
335         } else {
336             Map<String, ?> keyFieldValues = getDataObjectMetaDataService().getPrimaryKeyFieldValues(getDataObject());
337             for (Object keyValue : keyFieldValues.values()) {
338                 if (keyValue == null) {
339                     isOldDataObjectInExistence = false;
340                 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
341                     isOldDataObjectInExistence = false;
342                 }
343 
344                 if (!isOldDataObjectInExistence) {
345                     break;
346                 }
347             }
348         }
349 
350         return isOldDataObjectInExistence;
351     }
352 
353     /**
354      * @see org.kuali.rice.krad.maintenance.Maintainable#prepareForSave
355      */
356     @Override
357     public void prepareForSave() {
358         // by default do nothing
359     }
360 
361     /**
362      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterRetrieve
363      */
364     @Override
365     public void processAfterRetrieve() {
366         // by default do nothing
367     }
368 
369     /**
370      * @see org.kuali.rice.krad.maintenance.MaintainableImpl#setupNewFromExisting
371      */
372     @Override
373     public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
374         // by default do nothing
375     }
376 
377     /**
378      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterCopy
379      */
380     @Override
381     public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> requestParameters) {
382         // by default do nothing
383     }
384 
385     /**
386      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterEdit
387      */
388     @Override
389     public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
390         // by default do nothing
391     }
392 
393     /**
394      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterNew
395      */
396     @Override
397     public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
398         // by default do nothing
399     }
400 
401     /**
402      * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterPost
403      */
404     @Override
405     public void processAfterPost(MaintenanceDocument document, Map<String, String[]> requestParameters) {
406         // by default do nothing
407     }
408 
409     /**
410      * In the case of edit maintenance adds a new blank line to the old side
411      *
412      * TODO: should this write some sort of missing message on the old side
413      * instead?
414      *
415      * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View,
416      *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,
417      *      java.lang.Object)
418      */
419     @Override
420     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
421         super.processAfterAddLine(view, collectionGroup, model, addLine);
422         
423         // Check for maintenance documents in edit but exclude notes
424         if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction()) && !(addLine instanceof Note)) {
425             MaintenanceForm maintenanceForm = (MaintenanceForm) model;
426             MaintenanceDocument document = maintenanceForm.getDocument();
427 
428             // get the old object's collection
429             Collection<Object> oldCollection = ObjectPropertyUtils
430                     .getPropertyValue(document.getOldMaintainableObject().getDataObject(),
431                             collectionGroup.getPropertyName());
432             try {
433                 Object blankLine = collectionGroup.getCollectionObjectClass().newInstance();
434                 oldCollection.add(blankLine);
435             } catch (Exception e) {
436                 throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
437             }
438         }
439     }
440     
441     /**
442      * In the case of edit maintenance deleted the item on the old side
443      *
444      *
445      * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(View,
446      *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,  int)
447      */
448     @Override
449     protected void processAfterDeleteLine(View view, CollectionGroup collectionGroup, Object model, int lineIndex) {
450         super.processAfterDeleteLine(view, collectionGroup, model, lineIndex);
451         
452         // Check for maintenance documents in edit but exclude notes
453         if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction()) 
454                 && !collectionGroup.getCollectionObjectClass().getName().equals(Note.class.getName())) {
455             MaintenanceForm maintenanceForm = (MaintenanceForm) model;
456             MaintenanceDocument document = maintenanceForm.getDocument();
457 
458             // get the old object's collection
459             Collection<Object> oldCollection = ObjectPropertyUtils
460                     .getPropertyValue(document.getOldMaintainableObject().getDataObject(),
461                             collectionGroup.getPropertyName());
462             try {
463                 // Remove the object at lineIndex from the collection
464                 oldCollection.remove(oldCollection.toArray()[lineIndex]);
465             } catch (Exception e) {
466                 throw new RuntimeException("Unable to delete line instance for old maintenance object", e);
467             }
468         }
469     }    
470 
471     /**
472      * Retrieves the document number configured on this maintainable
473      *
474      * @return String document number
475      */
476     protected String getDocumentNumber() {
477         return this.documentNumber;
478     }
479 
480     protected LookupService getLookupService() {
481         if (lookupService == null) {
482             lookupService = KRADServiceLocatorWeb.getLookupService();
483         }
484         return this.lookupService;
485     }
486 
487     public void setLookupService(LookupService lookupService) {
488         this.lookupService = lookupService;
489     }
490 
491     protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
492         if (dataObjectAuthorizationService == null) {
493             this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService();
494         }
495         return dataObjectAuthorizationService;
496     }
497 
498     public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
499         this.dataObjectAuthorizationService = dataObjectAuthorizationService;
500     }
501 
502     protected DataObjectMetaDataService getDataObjectMetaDataService() {
503         if (dataObjectMetaDataService == null) {
504             this.dataObjectMetaDataService = KRADServiceLocatorWeb.getDataObjectMetaDataService();
505         }
506         return dataObjectMetaDataService;
507     }
508 
509     public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) {
510         this.dataObjectMetaDataService = dataObjectMetaDataService;
511     }
512 
513     public DocumentDictionaryService getDocumentDictionaryService() {
514         if (documentDictionaryService == null) {
515             this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
516         }
517         return documentDictionaryService;
518     }
519 
520     public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
521         this.documentDictionaryService = documentDictionaryService;
522     }
523 
524     protected EncryptionService getEncryptionService() {
525         if (encryptionService == null) {
526             encryptionService = CoreApiServiceLocator.getEncryptionService();
527         }
528         return encryptionService;
529     }
530 
531     public void setEncryptionService(EncryptionService encryptionService) {
532         this.encryptionService = encryptionService;
533     }
534 
535     protected BusinessObjectService getBusinessObjectService() {
536         if (businessObjectService == null) {
537             businessObjectService = KRADServiceLocator.getBusinessObjectService();
538         }
539         return businessObjectService;
540     }
541 
542     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
543         this.businessObjectService = businessObjectService;
544     }
545 
546     protected MaintenanceDocumentService getMaintenanceDocumentService() {
547         if (maintenanceDocumentService == null) {
548             maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService();
549         }
550         return maintenanceDocumentService;
551     }
552 
553     public void setMaintenanceDocumentService(MaintenanceDocumentService maintenanceDocumentService) {
554         this.maintenanceDocumentService = maintenanceDocumentService;
555     }
556 }