View Javadoc
1   /**
2    * Copyright 2005-2016 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.service.impl;
17  
18  import org.apache.commons.beanutils.PropertyUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.apache.ojb.broker.metadata.ClassDescriptor;
22  import org.apache.ojb.broker.metadata.ConnectionRepository;
23  import org.apache.ojb.broker.metadata.DescriptorRepository;
24  import org.apache.ojb.broker.metadata.FieldDescriptor;
25  import org.apache.ojb.broker.metadata.MetadataManager;
26  import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
27  import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
28  import org.kuali.rice.core.api.exception.RiceRuntimeException;
29  import org.kuali.rice.core.api.util.ClassLoaderUtils;
30  import org.kuali.rice.krad.bo.PersistableBusinessObject;
31  import org.kuali.rice.krad.dao.PersistenceDao;
32  import org.kuali.rice.krad.exception.IntrospectionException;
33  import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
34  import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
35  import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
36  import org.kuali.rice.krad.service.PersistenceService;
37  import org.kuali.rice.krad.util.LegacyDataFramework;
38  import org.kuali.rice.krad.util.ObjectUtils;
39  import org.springframework.core.io.DefaultResourceLoader;
40  import org.springframework.transaction.annotation.Transactional;
41  
42  import java.beans.PropertyDescriptor;
43  import java.io.IOException;
44  import java.io.InputStream;
45  import java.lang.reflect.InvocationTargetException;
46  import java.util.HashMap;
47  import java.util.HashSet;
48  import java.util.Iterator;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.Set;
52  import java.util.Vector;
53  
54  /**
55   * This class is the service implementation for the Persistence structure.
56   * OjbRepositoryExplorer provides functions for extracting information from the
57   * OJB repository at runtime. This is the default implementation, that is
58   * delivered with Kuali.
59   */
60  @Deprecated
61  @Transactional
62  @LegacyDataFramework
63  public class PersistenceServiceOjbImpl extends PersistenceServiceImplBase implements PersistenceService {
64      private static Logger LOG = Logger.getLogger(PersistenceServiceOjbImpl.class);
65      private static final String CLASSPATH_RESOURCE_PREFIX = "classpath:";
66      private PersistenceDao persistenceDao;
67  
68      @Override
69  	public void clearCache() {
70          persistenceDao.clearCache();
71      }
72  
73      @Override
74  	public Object resolveProxy(Object o) {
75          return persistenceDao.resolveProxy(o);
76      }
77  
78  	@Override
79  	public void loadRepositoryDescriptor(String ojbRepositoryFilePath) {
80  		if ( LOG.isInfoEnabled() ) {
81  			LOG.info("Begin loading OJB Metadata for: " + ojbRepositoryFilePath);
82  		}
83  		DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
84  		InputStream is = null;
85  		try {
86  			is = resourceLoader.getResource(CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath).getInputStream();
87  			ConnectionRepository cr = MetadataManager.getInstance().readConnectionRepository(is);
88  			MetadataManager.getInstance().mergeConnectionRepository(cr);
89  
90  			is = resourceLoader.getResource(CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath).getInputStream();
91  			DescriptorRepository dr = MetadataManager.getInstance().readDescriptorRepository(is);
92  			MetadataManager.getInstance().mergeDescriptorRepository(dr);
93  
94  			if (LOG.isDebugEnabled()) {
95  				LOG.debug("--------------------------------------------------------------------------");
96  				LOG.debug("Merging repository descriptor: " + ojbRepositoryFilePath);
97  				LOG.debug("--------------------------------------------------------------------------");
98  			}
99  		} 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 }