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