View Javadoc

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