001package org.kuali.rice.krad.service.impl; 002 003import org.apache.commons.beanutils.PropertyUtils; 004import org.apache.commons.lang.StringUtils; 005import org.kuali.rice.core.api.config.property.ConfigContext; 006import org.kuali.rice.kim.api.identity.Person; 007import org.kuali.rice.kim.api.identity.PersonService; 008import org.kuali.rice.kim.api.services.KimApiServiceLocator; 009import org.kuali.rice.krad.bo.*; 010import org.kuali.rice.krad.dao.BusinessObjectDao; 011import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException; 012import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException; 013import org.kuali.rice.krad.service.*; 014import org.kuali.rice.krad.util.KRADConstants; 015import org.kuali.rice.krad.util.LegacyDataFramework; 016import org.kuali.rice.krad.util.ObjectUtils; 017import org.springframework.transaction.annotation.Transactional; 018 019import java.beans.PropertyDescriptor; 020import java.util.*; 021 022/** 023 * Created by angelind on 11/6/14. 024 * 025 * Overridden by 'Sheik Salahudeen' 026 * Overridden for fixing the document fetching issue in KRAD Transaction document with OJB. 027 * Modified method names: findBySinglePrimaryKey,findByPrimaryKey,findAll,findAllOrderBy,findMatching,findMatchingOrderBy 028 * Changes description : Modified the return type of method from '<T extends BusinessObject>' to '<T>'. 029 */ 030@Deprecated 031@LegacyDataFramework 032public class BusinessObjectServiceImpl implements BusinessObjectService { 033 034 private PersistenceService persistenceService; 035 private PersistenceStructureService persistenceStructureService; 036 private BusinessObjectDao businessObjectDao; 037 private PersonService personService; 038 private DataObjectMetaDataService dataObjectMetaDataService; 039 private LegacyDataFramework legacyDataFramework; 040 041 private boolean illegalBusinessObjectsForSaveInitialized; 042 private final Set<String> illegalBusinessObjectsForSave = new HashSet<String>(); 043 044 @Override 045 @Transactional 046 public <T extends PersistableBusinessObject> T save(T bo) { 047 validateBusinessObjectForSave(bo); 048 return (T) businessObjectDao.save(bo); 049 } 050 051 @Override 052 @Transactional 053 public List<? extends PersistableBusinessObject> save(List<? extends PersistableBusinessObject> businessObjects) { 054 validateBusinessObjectForSave(businessObjects); 055 return businessObjectDao.save(businessObjects); 056 } 057 058 @Override 059 @Transactional 060 public PersistableBusinessObject linkAndSave(PersistableBusinessObject bo) { 061 validateBusinessObjectForSave(bo); 062 persistenceService.linkObjects(bo); 063 return businessObjectDao.save(bo); 064 } 065 066 @Override 067 @Transactional 068 public List<? extends PersistableBusinessObject> linkAndSave(List<? extends PersistableBusinessObject> businessObjects) { 069 validateBusinessObjectForSave(businessObjects); 070 return businessObjectDao.save(businessObjects); 071 } 072 073 protected void validateBusinessObjectForSave(PersistableBusinessObject bo) { 074 if (bo == null) { 075 throw new IllegalArgumentException("Object passed in is null"); 076 } 077 if (!isBusinessObjectAllowedForSave(bo)) { 078 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); 079 } 080 } 081 082 protected void validateBusinessObjectForSave(List<? extends PersistableBusinessObject> businessObjects) { 083 for (PersistableBusinessObject bo : businessObjects) { 084 if (bo == null) { 085 throw new IllegalArgumentException("One of the objects in the List is null."); 086 } 087 if (!isBusinessObjectAllowedForSave(bo)) { 088 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 089 + " Passed in type was '" + bo.getClass().getName() + "'."); 090 } 091 } 092 } 093 094 095 /** 096 * Returns true if the BusinessObjectService should be permitted to save instances of the given PersistableBusinessObject. 097 * Implementation checks a configuration parameter for class names of PersistableBusinessObjects that shouldn't be allowed 098 * to be saved. 099 */ 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