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