View Javadoc
1   package org.kuali.rice.krad.service.impl;
2   
3   import org.apache.commons.beanutils.PropertyUtils;
4   import org.apache.commons.lang.StringUtils;
5   import org.kuali.rice.core.api.config.property.ConfigContext;
6   import org.kuali.rice.kim.api.identity.Person;
7   import org.kuali.rice.kim.api.identity.PersonService;
8   import org.kuali.rice.kim.api.services.KimApiServiceLocator;
9   import org.kuali.rice.krad.bo.*;
10  import org.kuali.rice.krad.dao.BusinessObjectDao;
11  import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
12  import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
13  import org.kuali.rice.krad.service.*;
14  import org.kuali.rice.krad.util.KRADConstants;
15  import org.kuali.rice.krad.util.LegacyDataFramework;
16  import org.kuali.rice.krad.util.ObjectUtils;
17  import org.springframework.transaction.annotation.Transactional;
18  
19  import java.beans.PropertyDescriptor;
20  import java.util.*;
21  
22  /**
23   * Created by angelind on 11/6/14.
24   *
25   * Overridden by 'Sheik Salahudeen'
26   * Overridden for fixing the document fetching issue in KRAD Transaction document with OJB.
27   * Modified method names: findBySinglePrimaryKey,findByPrimaryKey,findAll,findAllOrderBy,findMatching,findMatchingOrderBy
28   * Changes description : Modified the return type of method from '<T extends BusinessObject>' to  '<T>'.
29   */
30  @Deprecated
31  @LegacyDataFramework
32  public class BusinessObjectServiceImpl implements BusinessObjectService {
33  
34      private PersistenceService persistenceService;
35      private PersistenceStructureService persistenceStructureService;
36      private BusinessObjectDao businessObjectDao;
37      private PersonService personService;
38      private DataObjectMetaDataService dataObjectMetaDataService;
39      private LegacyDataFramework legacyDataFramework;
40  
41      private boolean illegalBusinessObjectsForSaveInitialized;
42      private final Set<String> illegalBusinessObjectsForSave = new HashSet<String>();
43  
44      @Override
45      @Transactional
46      public <T extends PersistableBusinessObject> T save(T bo) {
47          validateBusinessObjectForSave(bo);
48          return (T) businessObjectDao.save(bo);
49      }
50  
51      @Override
52      @Transactional
53      public List<? extends PersistableBusinessObject> save(List<? extends PersistableBusinessObject> businessObjects) {
54          validateBusinessObjectForSave(businessObjects);
55          return businessObjectDao.save(businessObjects);
56      }
57  
58      @Override
59      @Transactional
60      public PersistableBusinessObject linkAndSave(PersistableBusinessObject bo) {
61          validateBusinessObjectForSave(bo);
62          persistenceService.linkObjects(bo);
63          return businessObjectDao.save(bo);
64      }
65  
66      @Override
67      @Transactional
68      public List<? extends PersistableBusinessObject> linkAndSave(List<? extends PersistableBusinessObject> businessObjects) {
69          validateBusinessObjectForSave(businessObjects);
70          return businessObjectDao.save(businessObjects);
71      }
72  
73      protected void validateBusinessObjectForSave(PersistableBusinessObject bo) {
74          if (bo == null) {
75              throw new IllegalArgumentException("Object passed in is null");
76          }
77          if (!isBusinessObjectAllowedForSave(bo)) {
78              throw new IllegalArgumentException("Object passed in is a BusinessObject but has been restricted from save operations according to configuration parameter '" + KRADConstants.Config.ILLEGAL_BUSINESS_OBJECTS_FOR_SAVE);
79          }
80      }
81  
82      protected void validateBusinessObjectForSave(List<? extends PersistableBusinessObject> businessObjects) {
83          for (PersistableBusinessObject bo : businessObjects) {
84              if (bo == null) {
85                  throw new IllegalArgumentException("One of the objects in the List is null.");
86              }
87              if (!isBusinessObjectAllowedForSave(bo)) {
88                  throw new IllegalArgumentException("One of the objects in the List is a BusinessObject but has been restricted from save operations according to configuration parameter '" + KRADConstants.Config.ILLEGAL_BUSINESS_OBJECTS_FOR_SAVE
89                          + "  Passed in type was '" + bo.getClass().getName() + "'.");
90              }
91          }
92      }
93  
94  
95      /**
96       * Returns true if the BusinessObjectService should be permitted to save instances of the given PersistableBusinessObject.
97       * Implementation checks a configuration parameter for class names of PersistableBusinessObjects that shouldn't be allowed
98       * to be saved.
99       */
100     protected boolean isBusinessObjectAllowedForSave(PersistableBusinessObject bo) {
101         if (!illegalBusinessObjectsForSaveInitialized) {
102             synchronized (this) {
103                 boolean applyCheck = true;
104                 String applyCheckValue = ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.Config.APPLY_ILLEGAL_BUSINESS_OBJECT_FOR_SAVE_CHECK);
105                 if (!StringUtils.isEmpty(applyCheckValue)) {
106                     applyCheck = Boolean.valueOf(applyCheckValue);
107                 }
108                 if (applyCheck) {
109                     String illegalBos = ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.Config.ILLEGAL_BUSINESS_OBJECTS_FOR_SAVE);
110                     if (!StringUtils.isEmpty(illegalBos)) {
111                         String[] illegalBosSplit = illegalBos.split(",");
112                         for (String illegalBo : illegalBosSplit) {
113                             illegalBusinessObjectsForSave.add(illegalBo.trim());
114                         }
115                     }
116                 }
117             }
118             illegalBusinessObjectsForSaveInitialized = true;
119         }
120         return !illegalBusinessObjectsForSave.contains(bo.getClass().getName());
121     }
122 
123 
124     @Override
125     public <T> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) {
126         return businessObjectDao.findBySinglePrimaryKey(clazz, primaryKey);
127     }
128     @Override
129     public <T> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) {
130         return businessObjectDao.findByPrimaryKey(clazz, primaryKeys);
131     }
132 
133     @Override
134     public Object retrieve(Object object) {
135         return businessObjectDao.retrieve(object);
136     }
137 
138     @Override
139     public <T> Collection<T> findAll(Class<T> clazz) {
140         return businessObjectDao.findAll(clazz);
141     }
142     @Override
143     public <T> Collection<T> findAllOrderBy( Class<T> clazz, String sortField, boolean sortAscending ) {
144         final Map<String, ?> emptyParameters = Collections.emptyMap();
145         return businessObjectDao.findMatchingOrderBy(clazz, emptyParameters, sortField, sortAscending );
146     }
147 
148     @Override
149     public <T> Collection<T> findMatching(Class<T> clazz, Map<String, ?> fieldValues) {
150         return businessObjectDao.findMatching(clazz, fieldValues);
151     }
152 
153     @Override
154     public int countMatching(Class clazz, Map<String, ?> fieldValues) {
155         return businessObjectDao.countMatching(clazz, fieldValues);
156     }
157 
158     @Override
159     public int countMatching(Class clazz, Map<String, ?> positiveFieldValues, Map<String, ?> negativeFieldValues) {
160         return businessObjectDao.countMatching(clazz, positiveFieldValues, negativeFieldValues);
161     }
162     @Override
163     public <T> Collection<T> findMatchingOrderBy(Class<T> clazz, Map<String, ?> fieldValues, String sortField, boolean sortAscending) {
164         return businessObjectDao.findMatchingOrderBy(clazz, fieldValues, sortField, sortAscending);
165     }
166     @Override
167     @Transactional
168     public void delete(Object bo) {
169         // just need to make sure erasure does not cause this method to attempt to process a list argument
170         if ( bo instanceof List ) {
171             delete( (List<PersistableBusinessObject>)bo );
172         } else {
173             businessObjectDao.delete(bo);
174         }
175     }
176 
177     @Override
178     @Transactional
179     public void delete(List<? extends PersistableBusinessObject> boList) {
180         businessObjectDao.delete(boList);
181     }
182 
183     @Override
184     @Transactional
185     public void deleteMatching(Class clazz, Map<String, ?> fieldValues) {
186         businessObjectDao.deleteMatching(clazz, fieldValues);
187     }
188 
189     @Override
190     public BusinessObject getReferenceIfExists(BusinessObject bo, String referenceName) {
191         // if either argument is null, then we have nothing to do, complain and abort
192         if (ObjectUtils.isNull(bo)) {
193             throw new IllegalArgumentException("Passed in BusinessObject was null.  No processing can be done.");
194         }
195         if (StringUtils.isEmpty(referenceName)) {
196             throw new IllegalArgumentException("Passed in referenceName was empty or null.  No processing can be done.");
197         }
198 
199         // make sure the attribute exists at all, throw exception if not
200         PropertyDescriptor propertyDescriptor;
201         try {
202             propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
203         }
204         catch (Exception e) {
205             throw new RuntimeException(e);
206         }
207         if (propertyDescriptor == null) {
208             throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'. GFK");
209         }
210 
211         // get the class of the attribute name
212         Class referenceClass = null;
213         if(bo instanceof PersistableBusinessObject) {
214             referenceClass = persistenceStructureService.getBusinessObjectAttributeClass(((PersistableBusinessObject)bo).getClass(), referenceName);
215         }
216         if(referenceClass == null) {
217             referenceClass = ObjectUtils.getPropertyType( bo, referenceName, persistenceStructureService );
218         }
219         if ( referenceClass == null ) {
220             referenceClass = propertyDescriptor.getPropertyType();
221         }
222 
223         /*
224          * check for Person or EBO references in which case we can just get the reference through propertyutils
225          */
226         if (ExternalizableBusinessObject.class.isAssignableFrom(referenceClass)) {
227             try {
228                 BusinessObject referenceBoExternalizable = (BusinessObject) PropertyUtils.getProperty(bo, referenceName);
229                 if (referenceBoExternalizable!=null) {
230                     return referenceBoExternalizable;
231                 }
232             } catch (Exception ex) {
233                 //throw new RuntimeException("Unable to get property " + referenceName + " from a BO of class: " + bo.getClass().getName(),ex);
234                 //Proceed further - get the BO relationship using responsible module service and proceed further
235             }
236         }
237 
238         // make sure the class of the attribute descends from BusinessObject,
239         // otherwise throw an exception
240         if (!ExternalizableBusinessObject.class.isAssignableFrom(referenceClass) && !PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
241             throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + referenceName + ") is of class: " + "'" + referenceClass.getName() + "' and is not a " + "descendent of PersistableBusinessObject.  Only descendents of PersistableBusinessObject " + "can be used.");
242         }
243 
244         // get the list of foreign-keys for this reference. if the reference
245         // does not exist, or is not a reference-descriptor, an exception will
246         // be thrown here.
247         //DataObjectRelationship boRel = dataObjectMetaDataService.getBusinessObjectRelationship( bo, referenceName );
248         DataObjectRelationship boRel = getDataObjectMetaDataService().getDataObjectRelationship(bo, bo.getClass(),
249                 referenceName, "", true, false, false);
250         final Map<String,String> fkMap = boRel != null ? boRel.getParentToChildReferences() : Collections.<String, String>emptyMap();
251 
252         boolean allFkeysHaveValues = true;
253         // walk through the foreign keys, testing each one to see if it has a value
254         Map<String,Object> pkMap = new HashMap<String,Object>();
255         for (Map.Entry<String, String> entry : fkMap.entrySet()) {
256             String fkFieldName = entry.getKey();
257             String pkFieldName = entry.getValue();
258 
259             // attempt to retrieve the value for the given field
260             Object fkFieldValue;
261             try {
262                 fkFieldValue = PropertyUtils.getProperty(bo, fkFieldName);
263             }
264             catch (Exception e) {
265                 throw new RuntimeException(e);
266             }
267 
268             // determine if there is a value for the field
269             if (ObjectUtils.isNull(fkFieldValue)) {
270                 allFkeysHaveValues = false;
271                 break; // no reason to continue processing the fkeys
272             }
273             else if (String.class.isAssignableFrom(fkFieldValue.getClass())) {
274                 if (StringUtils.isEmpty((String) fkFieldValue)) {
275                     allFkeysHaveValues = false;
276                     break;
277                 }
278                 else {
279                     pkMap.put(pkFieldName, fkFieldValue);
280                 }
281             }
282 
283             // if there is a value, grab it
284             else {
285                 pkMap.put(pkFieldName, fkFieldValue);
286             }
287         }
288 
289         BusinessObject referenceBo = null;
290         // only do the retrieval if all Foreign Keys have values
291         if (allFkeysHaveValues) {
292             if (ExternalizableBusinessObject.class.isAssignableFrom(referenceClass)) {
293                 ModuleService responsibleModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(referenceClass);
294                 if(responsibleModuleService!=null) {
295                     return responsibleModuleService.<ExternalizableBusinessObject>getExternalizableBusinessObject(referenceClass, pkMap);
296                 }
297             } else
298                 referenceBo = this.<BusinessObject>findByPrimaryKey(referenceClass, pkMap);
299         }
300 
301         // return what we have, it'll be null if it was never retrieved
302         return referenceBo;
303     }
304     @Override
305     public void linkUserFields(Object bo) {
306         if (bo == null) {
307             throw new IllegalArgumentException("bo passed in was null");
308         }
309         if ( bo instanceof List ) {
310             linkUserFieldsInBoList( (List<PersistableBusinessObject>) bo );
311         } else {
312             if ( bo instanceof PersistableBusinessObject ) {
313                 ((PersistableBusinessObject) bo).linkEditableUserFields();
314                 linkUserFieldsInBoList( Collections.<PersistableBusinessObject>singletonList( (PersistableBusinessObject)bo ) );
315             } else if ( bo instanceof PersistableBusinessObjectBaseAdapter) {
316                 ((PersistableBusinessObjectBaseAdapter) bo).linkEditableUserFields();
317             }
318         }
319     }
320 
321     protected void linkUserFieldsInBoList(List<PersistableBusinessObject> list) {
322 
323         // do nothing if there's nothing to process
324         if (list == null) {
325             throw new IllegalArgumentException("List of bos passed in was null");
326         } else if (list.isEmpty()) {
327             return;
328         }
329 
330         Person person;
331         for (Object obj : list) {
332             // just need to protect in case non PBO passed in
333             if ( !(obj instanceof PersistableBusinessObject) ) {
334                 continue;
335             }
336             PersistableBusinessObject bo = (PersistableBusinessObject) obj;
337             // get a list of the reference objects on the BO
338             List<DataObjectRelationship> relationships = dataObjectMetaDataService.getDataObjectRelationships(
339                     bo.getClass());
340             for ( DataObjectRelationship rel : relationships ) {
341                 if ( Person.class.isAssignableFrom( rel.getRelatedClass() ) ) {
342                     person = (Person) ObjectUtils.getPropertyValue(bo, rel.getParentAttributeName() );
343                     if (person != null) {
344                         // find the universal user ID relationship and link the field
345                         for ( Map.Entry<String,String> entry : rel.getParentToChildReferences().entrySet() ) {
346                             if ( "principalId".equals(entry.getValue())) {
347                                 linkUserReference(bo, person, rel.getParentAttributeName(), entry.getKey() );
348                                 break;
349                             }
350                         }
351                     }
352                 }
353             }
354             if ( persistenceStructureService.isPersistable(bo.getClass())) {
355                 Map<String, Class> references = persistenceStructureService.listReferenceObjectFields(bo);
356 
357                 // walk through the ref objects, only doing work if they are Person objects
358                 for ( Map.Entry<String, Class> entry : references.entrySet() ) {
359                     if (Person.class.isAssignableFrom(entry.getValue())) {
360                         person = (Person) ObjectUtils.getPropertyValue(bo, entry.getKey());
361                         if (person != null) {
362                             String fkFieldName = persistenceStructureService.getForeignKeyFieldName(bo.getClass(), entry.getKey(), "principalId");
363                             linkUserReference(bo, person, entry.getKey(), fkFieldName);
364                         }
365                     }
366                 }
367             }
368         }
369     }
370 
371     /**
372      *
373      * This method links a single UniveralUser back to the parent BO based on the authoritative principalName.
374      *
375      * @param bo
376      * @param refFieldName
377      */
378     private void linkUserReference(PersistableBusinessObject bo, Person user, String refFieldName, String fkFieldName) {
379 
380         // if the UserId field is blank, there's nothing we can do, so quit
381         if (StringUtils.isBlank(user.getPrincipalName())) {
382             return;
383         }
384 
385         // attempt to load the user from the user-name, exit quietly if the user isnt found
386         Person userFromService = getPersonService().getPersonByPrincipalName(user.getPrincipalName());
387         if (userFromService == null) {
388             return;
389         }
390 
391         // attempt to set the universalId on the parent BO
392         setBoField(bo, fkFieldName, userFromService.getPrincipalId());
393     }
394 
395     private void setBoField(PersistableBusinessObject bo, String fieldName, Object fieldValue) {
396         try {
397             ObjectUtils.setObjectProperty(bo, fieldName, fieldValue.getClass(), fieldValue);
398         }
399         catch (Exception e) {
400             throw new RuntimeException("Could not set field [" + fieldName + "] on BO to value: " + fieldValue.toString() + " (see nested exception for details).", e);
401         }
402     }
403 
404     @Override
405     public PersistableBusinessObject manageReadOnly(PersistableBusinessObject bo) {
406         return getBusinessObjectDao().manageReadOnly(bo);
407     }
408 
409     /**
410      * Gets the businessObjectDao attribute.
411      *
412      * @return Returns the businessObjectDao.
413      */
414     protected BusinessObjectDao getBusinessObjectDao() {
415         return businessObjectDao;
416     }
417 
418     /**
419      * Sets the businessObjectDao attribute value.
420      *
421      * @param businessObjectDao The businessObjectDao to set.
422      */
423     public void setBusinessObjectDao(BusinessObjectDao businessObjectDao) {
424         this.businessObjectDao = businessObjectDao;
425     }
426 
427     /**
428      * Sets the persistenceStructureService attribute value.
429      *
430      * @param persistenceStructureService The persistenceStructureService to set.
431      */
432     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
433         this.persistenceStructureService = persistenceStructureService;
434     }
435 
436     /**
437      * Sets the kualiUserService attribute value.
438      */
439     public final void setPersonService(PersonService personService) {
440         this.personService = personService;
441     }
442 
443     protected PersonService getPersonService() {
444         return personService != null ? personService : (personService = KimApiServiceLocator.getPersonService());
445     }
446 
447     /**
448      * Sets the persistenceService attribute value.
449      *
450      * @param persistenceService The persistenceService to set.
451      */
452     public final void setPersistenceService(PersistenceService persistenceService) {
453         this.persistenceService = persistenceService;
454     }
455 
456     protected DataObjectMetaDataService getDataObjectMetaDataService() {
457         return dataObjectMetaDataService;
458     }
459 
460     public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetadataService) {
461         this.dataObjectMetaDataService = dataObjectMetadataService;
462     }
463 
464 }
465