001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.service.impl; 017 018import org.apache.commons.beanutils.PropertyUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.log4j.Logger; 021import org.apache.ojb.broker.metadata.ClassDescriptor; 022import org.apache.ojb.broker.metadata.ConnectionRepository; 023import org.apache.ojb.broker.metadata.DescriptorRepository; 024import org.apache.ojb.broker.metadata.FieldDescriptor; 025import org.apache.ojb.broker.metadata.MetadataManager; 026import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; 027import org.apache.ojb.broker.metadata.fieldaccess.PersistentField; 028import org.kuali.rice.core.api.exception.RiceRuntimeException; 029import org.kuali.rice.core.api.util.ClassLoaderUtils; 030import org.kuali.rice.krad.bo.PersistableBusinessObject; 031import org.kuali.rice.krad.dao.PersistenceDao; 032import org.kuali.rice.krad.exception.IntrospectionException; 033import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException; 034import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException; 035import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException; 036import org.kuali.rice.krad.service.PersistenceService; 037import org.kuali.rice.krad.util.ObjectUtils; 038import org.springframework.core.io.DefaultResourceLoader; 039import org.springframework.transaction.annotation.Transactional; 040 041import java.beans.PropertyDescriptor; 042import java.io.IOException; 043import java.io.InputStream; 044import java.lang.reflect.InvocationTargetException; 045import java.util.HashMap; 046import java.util.HashSet; 047import java.util.Iterator; 048import java.util.List; 049import java.util.Map; 050import java.util.Set; 051import java.util.Vector; 052 053/** 054 * This class is the service implementation for the Persistence structure. 055 * OjbRepositoryExplorer provides functions for extracting information from the 056 * OJB repository at runtime. This is the default implementation, that is 057 * delivered with Kuali. 058 */ 059@Transactional 060public class PersistenceServiceOjbImpl extends PersistenceServiceImplBase implements PersistenceService { 061 private static Logger LOG = Logger.getLogger(PersistenceServiceOjbImpl.class); 062 private static final String CLASSPATH_RESOURCE_PREFIX = "classpath:"; 063 private PersistenceDao persistenceDao; 064 065 public void clearCache() { 066 persistenceDao.clearCache(); 067 } 068 069 public Object resolveProxy(Object o) { 070 return persistenceDao.resolveProxy(o); 071 } 072 073 public void loadRepositoryDescriptor(String ojbRepositoryFilePath) { 074 if ( LOG.isInfoEnabled() ) { 075 LOG.info("Begin loading OJB Metadata for: " + ojbRepositoryFilePath); 076 } 077 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader()); 078 InputStream is = null; 079 try { 080 is = resourceLoader.getResource(CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath).getInputStream(); 081 ConnectionRepository cr = MetadataManager.getInstance().readConnectionRepository(is); 082 MetadataManager.getInstance().mergeConnectionRepository(cr); 083 084 is = resourceLoader.getResource(CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath).getInputStream(); 085 DescriptorRepository dr = MetadataManager.getInstance().readDescriptorRepository(is); 086 MetadataManager.getInstance().mergeDescriptorRepository(dr); 087 088 if (LOG.isDebugEnabled()) { 089 LOG.debug("--------------------------------------------------------------------------"); 090 LOG.debug("Merging repository descriptor: " + ojbRepositoryFilePath); 091 LOG.debug("--------------------------------------------------------------------------"); 092 } 093 } catch (IOException ioe) { 094 if (is != null) { 095 try { 096 is.close(); 097 } catch (IOException e) { 098 LOG.warn("Failed to close InputStream on OJB repository path " + ojbRepositoryFilePath, e); 099 } 100 } 101 throw new RiceRuntimeException(ioe); 102 } finally { 103 if (is != null) { 104 try { 105 is.close(); 106 } catch (IOException e) { 107 LOG.warn("Failed to close InputStream on OJB repository path " + ojbRepositoryFilePath, e); 108 } 109 } 110 } 111 if ( LOG.isInfoEnabled() ) { 112 LOG.info("Finished loading OJB Metadata for: " + ojbRepositoryFilePath); 113 } 114 } 115 116 /** 117 * @see org.kuali.rice.krad.service.PersistenceService#retrieveNonKeyFields(java.lang.Object) 118 */ 119 public void retrieveNonKeyFields(Object persistableObject) { 120 if (persistableObject == null) { 121 throw new IllegalArgumentException("invalid (null) persistableObject"); 122 } 123 if ( LOG.isDebugEnabled() ) { 124 LOG.debug("retrieving non-key fields for " + persistableObject); 125 } 126 127 persistenceDao.retrieveAllReferences(persistableObject); 128 } 129 130 /** 131 * @see org.kuali.rice.krad.service.PersistenceService#retrieveReferenceObject(java.lang.Object, 132 * String referenceObjectName) 133 */ 134 public void retrieveReferenceObject(Object persistableObject, String referenceObjectName) { 135 if (persistableObject == null) { 136 throw new IllegalArgumentException("invalid (null) persistableObject"); 137 } 138 if ( LOG.isDebugEnabled() ) { 139 LOG.debug("retrieving reference object " + referenceObjectName + " for " + persistableObject); 140 } 141 persistenceDao.retrieveReference(persistableObject, referenceObjectName); 142 } 143 144 /** 145 * @see org.kuali.rice.krad.service.PersistenceService#retrieveReferenceObject(java.lang.Object, 146 * String referenceObjectName) 147 */ 148 public void retrieveReferenceObjects(Object persistableObject, List referenceObjectNames) { 149 if (persistableObject == null) { 150 throw new IllegalArgumentException("invalid (null) persistableObject"); 151 } 152 if (referenceObjectNames == null) { 153 throw new IllegalArgumentException("invalid (null) referenceObjectNames"); 154 } 155 if (referenceObjectNames.isEmpty()) { 156 throw new IllegalArgumentException("invalid (empty) referenceObjectNames"); 157 } 158 159 int index = 0; 160 for (Iterator i = referenceObjectNames.iterator(); i.hasNext(); index++) { 161 String referenceObjectName = (String) i.next(); 162 if (StringUtils.isBlank(referenceObjectName)) { 163 throw new IllegalArgumentException("invalid (blank) name at position " + index); 164 } 165 166 retrieveReferenceObject(persistableObject, referenceObjectName); 167 } 168 } 169 170 /** 171 * @see org.kuali.rice.krad.service.PersistenceService#retrieveReferenceObject(java.lang.Object, 172 * String referenceObjectName) 173 */ 174 public void retrieveReferenceObjects(List persistableObjects, List referenceObjectNames) { 175 if (persistableObjects == null) { 176 throw new IllegalArgumentException("invalid (null) persistableObjects"); 177 } 178 if (persistableObjects.isEmpty()) { 179 throw new IllegalArgumentException("invalid (empty) persistableObjects"); 180 } 181 if (referenceObjectNames == null) { 182 throw new IllegalArgumentException("invalid (null) referenceObjectNames"); 183 } 184 if (referenceObjectNames.isEmpty()) { 185 throw new IllegalArgumentException("invalid (empty) referenceObjectNames"); 186 } 187 188 for (Iterator i = persistableObjects.iterator(); i.hasNext();) { 189 Object persistableObject = i.next(); 190 retrieveReferenceObjects(persistableObject, referenceObjectNames); 191 } 192 } 193 194 195 /** 196 * @see org.kuali.rice.krad.service.PersistenceService#getFlattenedPrimaryKeyFieldValues(java.lang.Object) 197 */ 198 public String getFlattenedPrimaryKeyFieldValues(Object persistableObject) { 199 if (persistableObject == null) { 200 throw new IllegalArgumentException("invalid (null) persistableObject"); 201 } 202 Map primaryKeyValues = getPrimaryKeyFieldValues(persistableObject, true); 203 204 StringBuffer flattened = new StringBuffer(persistableObject.getClass().getName()); 205 flattened.append("("); 206 for (Iterator i = primaryKeyValues.entrySet().iterator(); i.hasNext();) { 207 Map.Entry e = (Map.Entry) i.next(); 208 209 String fieldName = (String) e.getKey(); 210 Object fieldValue = e.getValue(); 211 212 flattened.append(fieldName + "=" + fieldValue); 213 if (i.hasNext()) { 214 flattened.append(","); 215 } 216 } 217 218 flattened.append(")"); 219 220 return flattened.toString(); 221 222 } 223 224 private void linkObjectsWithCircularReferenceCheck(Object persistableObject, Set referenceSet) { 225 if (ObjectUtils.isNull(persistableObject) || referenceSet.contains(persistableObject)) { 226 return; 227 } 228 referenceSet.add(persistableObject); 229 ClassDescriptor classDescriptor = getClassDescriptor(persistableObject.getClass()); 230 231 String className = null; 232 String fieldName = null; 233 try { 234 // iterate through all object references for the persistableObject 235 Vector objectReferences = classDescriptor.getObjectReferenceDescriptors(); 236 for (Iterator iter = objectReferences.iterator(); iter.hasNext();) { 237 ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter.next(); 238 239 // get the actual reference object 240 className = persistableObject.getClass().getName(); 241 fieldName = referenceDescriptor.getAttributeName(); 242 Object referenceObject = PropertyUtils.getProperty(persistableObject, fieldName); 243 if (ObjectUtils.isNull(referenceObject) || referenceSet.contains(referenceObject)) { 244 continue; 245 } 246 247 // recursively link object 248 linkObjectsWithCircularReferenceCheck(referenceObject, referenceSet); 249 250 // iterate through the keys for the reference object and set 251 // value 252 FieldDescriptor[] refFkNames = referenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor); 253 ClassDescriptor refCld = getClassDescriptor(referenceDescriptor.getItemClass()); 254 FieldDescriptor[] refPkNames = refCld.getPkFields(); 255 256 Map objFkValues = new HashMap(); 257 for (int i = 0; i < refPkNames.length; i++) { 258 objFkValues.put(refFkNames[i].getAttributeName(), ObjectUtils.getPropertyValue(referenceObject, refPkNames[i].getAttributeName())); 259 } 260 261 for (int i = 0; i < refFkNames.length; i++) { 262 FieldDescriptor fkField = refFkNames[i]; 263 String fkName = fkField.getAttributeName(); 264 265 // if the fk from object and use if main object does not 266 // have value 267 Object fkValue = null; 268 if (objFkValues.containsKey(fkName)) { 269 fkValue = objFkValues.get(fkName); 270 } 271 272 // if fk is set in main object, take value from there 273 Object mainFkValue = ObjectUtils.getPropertyValue(persistableObject, fkName); 274 if (ObjectUtils.isNotNull(mainFkValue) && StringUtils.isNotBlank(mainFkValue.toString())) { 275 fkValue = mainFkValue; 276 } else if (ObjectUtils.isNull(fkValue) || StringUtils.isBlank(fkValue.toString())) { 277 // find the value from one of the other reference 278 // objects 279 for (Iterator iter2 = objectReferences.iterator(); iter2.hasNext();) { 280 ObjectReferenceDescriptor checkDescriptor = (ObjectReferenceDescriptor) iter2.next(); 281 282 fkValue = getReferenceFKValue(persistableObject, checkDescriptor, fkName); 283 if (ObjectUtils.isNotNull(fkValue) && StringUtils.isNotBlank(fkValue.toString())) { 284 break; 285 } 286 } 287 } 288 289 // set the fk value 290 if (ObjectUtils.isNotNull(fkValue)) { 291 fieldName = refPkNames[i].getAttributeName(); 292 ObjectUtils.setObjectProperty(referenceObject, fieldName, fkValue.getClass(), fkValue); 293 294 // set fk in main object 295 if (ObjectUtils.isNull(mainFkValue)) { 296 ObjectUtils.setObjectProperty(persistableObject, fkName, fkValue.getClass(), fkValue); 297 } 298 } 299 } 300 } 301 } catch (NoSuchMethodException e) { 302 throw new IntrospectionException("no setter for property '" + className + "." + fieldName + "'", e); 303 } catch (IllegalAccessException e) { 304 throw new IntrospectionException("problem accessing property '" + className + "." + fieldName + "'", e); 305 } catch (InvocationTargetException e) { 306 throw new IntrospectionException("problem invoking getter for property '" + className + "." + fieldName + "'", e); 307 } 308 } 309 310 /** 311 * For each reference object to the parent persistableObject, sets the key 312 * values for that object. First, if the reference object already has a 313 * value for the key, the value is left unchanged. Otherwise, for 314 * non-anonymous keys, the value is taken from the parent object. For 315 * anonymous keys, all other persistableObjects are checked until a value 316 * for the key is found. 317 * 318 * @see org.kuali.rice.krad.service.PersistenceService#getReferencedObject(java.lang.Object, 319 * org.apache.ojb.broker.metadata.ObjectReferenceDescriptor) 320 */ 321 public void linkObjects(Object persistableObject) { 322 linkObjectsWithCircularReferenceCheck(persistableObject, new HashSet()); 323 } 324 325 /** 326 * 327 * @see org.kuali.rice.krad.service.PersistenceService#allForeignKeyValuesPopulatedForReference(org.kuali.rice.krad.bo.BusinessObject, 328 * java.lang.String) 329 */ 330 public boolean allForeignKeyValuesPopulatedForReference(PersistableBusinessObject bo, String referenceName) { 331 332 boolean allFkeysHaveValues = true; 333 334 // yelp if nulls were passed in 335 if (bo == null) { 336 throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null."); 337 } 338 if (StringUtils.isBlank(referenceName)) { 339 throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty."); 340 } 341 342 PropertyDescriptor propertyDescriptor = null; 343 344 // make sure the attribute exists at all, throw exception if not 345 try { 346 propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName); 347 } catch (Exception e) { 348 throw new RuntimeException(e); 349 } 350 if (propertyDescriptor == null) { 351 throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'."); 352 } 353 354 // get the class of the attribute name 355 Class referenceClass = getBusinessObjectAttributeClass( bo.getClass(), referenceName ); 356 if ( referenceClass == null ) { 357 referenceClass = propertyDescriptor.getPropertyType(); 358 } 359 360 // make sure the class of the attribute descends from BusinessObject, 361 // otherwise throw an exception 362 if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) { 363 throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + referenceName + ") is of class: " + "'" + referenceClass.getName() + "' and is not a " + "descendent of BusinessObject. Only descendents of BusinessObject " 364 + "can be used."); 365 } 366 367 // make sure the attribute designated is listed as a 368 // reference-descriptor 369 // on the clazz specified, otherwise throw an exception (OJB); 370 ClassDescriptor classDescriptor = getClassDescriptor(bo.getClass()); 371 ObjectReferenceDescriptor referenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(referenceName); 372 if (referenceDescriptor == null) { 373 throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'"); 374 } 375 376 // get the list of the foreign-keys for this reference-descriptor 377 // (OJB) 378 Vector fkFields = referenceDescriptor.getForeignKeyFields(); 379 Iterator fkIterator = fkFields.iterator(); 380 381 // walk through the list of the foreign keys, get their types 382 while (fkIterator.hasNext()) { 383 384 // get the field name of the fk & pk field 385 String fkFieldName = (String) fkIterator.next(); 386 387 // get the value for the fk field 388 Object fkFieldValue = null; 389 try { 390 fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName); 391 } 392 393 // if we cant retrieve the field value, then 394 // it doesnt have a value 395 catch (IllegalAccessException e) { 396 return false; 397 } catch (InvocationTargetException e) { 398 return false; 399 } catch (NoSuchMethodException e) { 400 return false; 401 } 402 403 // test the value 404 if (fkFieldValue == null) { 405 return false; 406 } else if (String.class.isAssignableFrom(fkFieldValue.getClass())) { 407 if (StringUtils.isBlank((String) fkFieldValue)) { 408 return false; 409 } 410 } 411 } 412 413 return allFkeysHaveValues; 414 } 415 416 /** 417 * 418 * @see org.kuali.rice.krad.service.PersistenceService#refreshAllNonUpdatingReferences(org.kuali.rice.krad.bo.BusinessObject) 419 */ 420 public void refreshAllNonUpdatingReferences(PersistableBusinessObject bo) { 421 422 // get the OJB class-descriptor for the bo class 423 ClassDescriptor classDescriptor = getClassDescriptor(bo.getClass()); 424 425 // get a list of all reference-descriptors for that class 426 Vector references = classDescriptor.getObjectReferenceDescriptors(); 427 428 // walk through all of the reference-descriptors 429 for (Iterator iter = references.iterator(); iter.hasNext();) { 430 ObjectReferenceDescriptor reference = (ObjectReferenceDescriptor) iter.next(); 431 432 // if its NOT an updateable reference, then lets refresh it 433 if (reference.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_NONE) { 434 PersistentField persistentField = reference.getPersistentField(); 435 String referenceName = persistentField.getName(); 436 retrieveReferenceObject(bo, referenceName); 437 } 438 } 439 } 440 441 private Object getReferenceFKValue(Object persistableObject, ObjectReferenceDescriptor chkRefCld, String fkName) { 442 ClassDescriptor classDescriptor = getClassDescriptor(persistableObject.getClass()); 443 Object referenceObject = ObjectUtils.getPropertyValue(persistableObject, chkRefCld.getAttributeName()); 444 445 if (referenceObject == null) { 446 return null; 447 } 448 449 FieldDescriptor[] refFkNames = chkRefCld.getForeignKeyFieldDescriptors(classDescriptor); 450 ClassDescriptor refCld = getClassDescriptor(chkRefCld.getItemClass()); 451 FieldDescriptor[] refPkNames = refCld.getPkFields(); 452 453 454 Object fkValue = null; 455 for (int i = 0; i < refFkNames.length; i++) { 456 FieldDescriptor fkField = refFkNames[i]; 457 458 if (fkField.getAttributeName().equals(fkName)) { 459 fkValue = ObjectUtils.getPropertyValue(referenceObject, refPkNames[i].getAttributeName()); 460 break; 461 } 462 } 463 464 return fkValue; 465 } 466 467 /** 468 * Asks persistenceDao if this represents a proxy 469 * 470 * @see org.kuali.rice.krad.service.PersistenceService#isProxied(java.lang.Object) 471 */ 472 public boolean isProxied(Object object) { 473 return persistenceDao.isProxied(object); 474 } 475 476 /** 477 * Sets the persistenceDao attribute value. 478 * 479 * @param persistenceDao 480 * The persistenceDao to set. 481 */ 482 public void setPersistenceDao(PersistenceDao persistenceDao) { 483 this.persistenceDao = persistenceDao; 484 } 485}