View Javadoc
1   /**
2    * Copyright 2005-2015 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 java.beans.PropertyDescriptor;
19  import java.lang.reflect.InvocationTargetException;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Vector;
27  
28  import org.apache.commons.beanutils.PropertyUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.ojb.broker.metadata.ClassDescriptor;
31  import org.apache.ojb.broker.metadata.CollectionDescriptor;
32  import org.apache.ojb.broker.metadata.FieldDescriptor;
33  import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
34  import org.apache.ojb.broker.metadata.SuperReferenceDescriptor;
35  import org.kuali.rice.krad.bo.DataObjectRelationship;
36  import org.kuali.rice.krad.bo.PersistableBusinessObject;
37  import org.kuali.rice.krad.bo.PersistableBusinessObjectBaseAdapter;
38  import org.kuali.rice.krad.exception.ClassNotPersistableException;
39  import org.kuali.rice.krad.exception.IntrospectionException;
40  import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
41  import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
42  import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
43  import org.kuali.rice.krad.service.PersistenceStructureService;
44  import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState;
45  import org.kuali.rice.krad.util.LegacyDataFramework;
46  @Deprecated
47  @LegacyDataFramework
48  public class PersistenceStructureServiceOjbImpl extends PersistenceServiceImplBase implements PersistenceStructureService {
49  
50  	/**
51  	 *
52  	 * special case when the attributeClass passed in doesnt match the class of
53  	 * the reference-descriptor as defined in ojb-repository. Currently the only
54  	 * case of this happening is ObjectCode vs. ObjectCodeCurrent.
55  	 *
56  	 * NOTE: This method makes no real sense and is a product of a hack
57  	 * introduced by KFS for an unknown reason. If you find yourself using this
58  	 * map stop and go do something else.
59  	 *
60  	 * @param from
61  	 *            the class in the code
62  	 * @param to
63  	 *            the class in the repository
64  	 */
65  	public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();
66  
67  
68  	private PersistenceStructureService persistenceStructureServiceJpa;
69  
70  	public PersistenceStructureService getPersistenceStructureServiceJpa() {
71  		return this.persistenceStructureServiceJpa;
72  	}
73  
74  	public void setPersistenceStructureServiceJpa(PersistenceStructureService persistenceStructureServiceJpa) {
75  		this.persistenceStructureServiceJpa = persistenceStructureServiceJpa;
76  	}
77  
78  	/**
79  	 * @see org.kuali.rice.krad.service.PersistenceService#isPersistable(java.lang.Class)
80  	 */
81  
82  	@Override
83  	public boolean isPersistable(Class clazz) {
84  		boolean isPersistable = false;
85  		try {
86  			if (getClassDescriptor(clazz) != null) {
87  				isPersistable = true;
88  			}
89  		} catch (ClassNotPersistableException e) {
90  			isPersistable = false;
91  		}
92  		return isPersistable;
93  	}
94  
95  	/**
96  	 * @see org.kuali.rice.krad.service.PersistenceService#getPrimaryKeys(java.lang.Class)
97  	 */
98  
99  	@Override
100 	public List getPrimaryKeys(Class clazz) {
101 		List pkList = new ArrayList();
102 		ClassDescriptor classDescriptor = getClassDescriptor(clazz);
103 		FieldDescriptor keyDescriptors[] = classDescriptor.getPkFields();
104 		for (int i = 0; i < keyDescriptors.length; ++i) {
105 			FieldDescriptor keyDescriptor = keyDescriptors[i];
106 			pkList.add(keyDescriptor.getAttributeName());
107 		}
108 		return pkList;
109 	}
110 
111 	/**
112 	 * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listFieldNames(java.lang.Class)
113 	 */
114 
115 	@Override
116 	public List listFieldNames(Class clazz) {
117 		List fieldNames = new ArrayList();
118 		ClassDescriptor classDescriptor = getClassDescriptor(clazz);
119 		FieldDescriptor fieldDescriptors[] = classDescriptor.getFieldDescriptions();
120 		for (int i = 0; i < fieldDescriptors.length; ++i) {
121 			FieldDescriptor fieldDescriptor = fieldDescriptors[i];
122 			fieldNames.add(fieldDescriptor.getAttributeName());
123 		}
124 		return fieldNames;
125 	}
126 
127 	/**
128 	 * @see org.kuali.rice.krad.service.PersistenceMetadataService#clearPrimaryKeyFields(java.lang.Object)
129 	 */
130 	// Unit tests only
131 	@Override
132 	public Object clearPrimaryKeyFields(Object persistableObject) {
133 		if (persistableObject == null) {
134 			throw new IllegalArgumentException("invalid (null) persistableObject");
135 		}
136 
137 		String className = null;
138 		String fieldName = null;
139 		try {
140 			className = persistableObject.getClass().getName();
141 			List fields = listPrimaryKeyFieldNames(persistableObject.getClass());
142 			for (Iterator i = fields.iterator(); i.hasNext();) {
143 				fieldName = (String) i.next();
144 
145 				PropertyUtils.setProperty(persistableObject, fieldName, null);
146 			}
147 
148 			if (persistableObject instanceof PersistableBusinessObject) {
149 				((PersistableBusinessObject) persistableObject).setObjectId(null);
150 			}
151 		} catch (NoSuchMethodException e) {
152 			throw new IntrospectionException("no setter for property '" + className + "." + fieldName + "'", e);
153 		} catch (IllegalAccessException e) {
154 			throw new IntrospectionException("problem accessing property '" + className + "." + fieldName + "'", e);
155 		} catch (InvocationTargetException e) {
156 			throw new IntrospectionException("problem invoking getter for property '" + className + "." + fieldName + "'", e);
157 		}
158 
159 		return persistableObject;
160 	}
161 
162 	/**
163 	 * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listPersistableSubclasses(java.lang.Class)
164 	 */
165 
166 	// Unit tests only
167 	@Override
168 	public List listPersistableSubclasses(Class superclazz) {
169 		if (superclazz == null) {
170 			throw new IllegalArgumentException("invalid (null) uberclass");
171 		}
172 
173 		Map allDescriptors = getDescriptorRepository().getDescriptorTable();
174 		List persistableSubclasses = new ArrayList();
175 		for (Iterator i = allDescriptors.entrySet().iterator(); i.hasNext();) {
176 			Map.Entry e = (Map.Entry) i.next();
177 
178 			Class persistableClass = ((ClassDescriptor) e.getValue()).getClassOfObject();
179 			if (!superclazz.equals(persistableClass) && superclazz.isAssignableFrom(persistableClass)) {
180 				persistableSubclasses.add(persistableClass);
181 			}
182 		}
183 		return persistableSubclasses;
184 	}
185 
186 	/**
187 	 * @see org.kuali.rice.krad.service.PersistenceService#getRelationshipMetadata(java.lang.Class,
188 	 *      java.lang.String)
189 	 */
190 
191 	@Override
192 	public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName, String attributePrefix) {
193 		if (persistableClass == null) {
194 			throw new IllegalArgumentException("invalid (null) persistableClass");
195 		}
196 		if (StringUtils.isBlank(attributeName)) {
197 			throw new IllegalArgumentException("invalid (blank) attributeName");
198 		}
199 
200 		Map<String, DataObjectRelationship> relationships = new HashMap<String, DataObjectRelationship>();
201 		ClassDescriptor classDescriptor = getClassDescriptor(persistableClass);
202 		Vector<ObjectReferenceDescriptor> references = classDescriptor.getObjectReferenceDescriptors();
203 		for (ObjectReferenceDescriptor objRef : references) {
204 			Vector fks = objRef.getForeignKeyFields();
205 			if (fks.contains(attributeName) || objRef.getAttributeName().equals(attributeName)) {
206 				Map<String, String> fkToPkRefs = getForeignKeysForReference(persistableClass, objRef.getAttributeName());
207 				DataObjectRelationship rel = new DataObjectRelationship(persistableClass, objRef.getAttributeName(), objRef.getItemClass());
208 				for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
209 					if (StringUtils.isBlank(attributePrefix)) {
210 						rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
211 					} else {
212 						rel.getParentToChildReferences().put(attributePrefix + "." + ref.getKey(), ref.getValue());
213 					}
214 				}
215 				relationships.put(objRef.getAttributeName(), rel);
216 			}
217 		}
218 		return relationships;
219 	}
220 
221 
222 	// Unit tests only
223 	@Override
224 	public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName) {
225 		return getRelationshipMetadata(persistableClass, attributeName, null);
226 	}
227 
228 	/**
229 	 * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldName(java.lang.Object,
230 	 *      java.lang.String, java.lang.String)
231 	 */
232 
233 	@Override
234 	public String getForeignKeyFieldName(Class persistableObjectClass, String attributeName, String pkName) {
235 		String fkName = "";
236 		ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
237 		ObjectReferenceDescriptor objectReferenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(attributeName);
238 		if (objectReferenceDescriptor == null) {
239 			throw new RuntimeException("Attribute name " + attributeName + " is not a valid reference to class " + persistableObjectClass.getName());
240 		}
241 		ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());
242 
243 		FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
244 		FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
245 		for (int i = 0; i < pkFields.length; i++) {
246 			FieldDescriptor pkField = pkFields[i];
247 			if (pkField.getAttributeName().equals(pkName)) {
248 				fkName = fkFields[i].getAttributeName();
249 			}
250 		}
251 		return fkName;
252 	}
253 
254 	/**
255 	 * @see org.kuali.rice.krad.service.PersistenceService#getReferencesForForeignKey(java.lang.Class,
256 	 *      java.lang.String)
257 	 */
258 
259 	@Override
260 	public Map getReferencesForForeignKey(Class persistableObjectClass, String attributeName) {
261 		Map referenceClasses = new HashMap();
262 		if (PersistableBusinessObject.class.isAssignableFrom(persistableObjectClass)) {
263 			ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
264 			Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
265 			for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
266 				ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter.next();
267 
268 				/*
269 				 * iterate through the fk keys for the reference object and if
270 				 * matches the attributeName add the class as a reference
271 				 */
272 				FieldDescriptor[] refFkNames = referenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
273 				for (int i = 0; i < refFkNames.length; i++) {
274 					FieldDescriptor fkField = refFkNames[i];
275 					if (fkField.getAttributeName().equals(attributeName)) {
276 						referenceClasses.put(referenceDescriptor.getAttributeName(), referenceDescriptor.getItemClass());
277 					}
278 				}
279 			}
280 		}
281 		return referenceClasses;
282 	}
283 
284 	/**
285 	 * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeysForReference(java.lang.Class,
286 	 *      java.lang.String) The Map structure is: Key(String fkFieldName) =>
287 	 *      Value(String pkFieldName) NOTE that this implementation depends on
288 	 *      the ordering of foreign-key elements in the ojb-repository matching
289 	 *      the ordering of primary-key declarations of the class on the other
290 	 *      side of the relationship. This is done because: 1. The current
291 	 *      version of OJB requires you to declare all of these things in the
292 	 *      correct (and matching) order in the ojb-repository file for it to
293 	 *      work at all. 2. There is no other way to match a given foreign-key
294 	 *      reference to its corresponding primary-key on the opposing side of
295 	 *      the relationship. Yes, this is a crummy way to do it, but OJB doesnt
296 	 *      provide explicit matches of foreign-keys to primary keys, and always
297 	 *      assumes that foreign-keys map to primary keys on the other object,
298 	 *      and never to a set of candidate keys, or any other column.
299 	 */
300 
301 	@Override
302 	public Map getForeignKeysForReference(Class clazz, String attributeName) {
303 		// yelp if nulls were passed in
304 		if (clazz == null) {
305 			throw new IllegalArgumentException("The Class passed in for the clazz argument was null.");
306 		}
307 		if (attributeName == null) {
308 			throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
309 		}
310 
311 		// get the class of the attribute name
312 		Class attributeClass = getBusinessObjectAttributeClass(clazz, attributeName);
313 		if (attributeClass == null) {
314 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + attributeName + "' does not exist " + "on class: '" + clazz.getName() + "'.");
315 		}
316 
317         // make sure the class of the attribute descends from BusinessObject,
318         // otherwise throw an exception
319         if ((!PersistableBusinessObject.class.isAssignableFrom(attributeClass)) &&
320             (!PersistableBusinessObjectBaseAdapter.class.isAssignableFrom(attributeClass))) {
321                 throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + attributeName +
322                 ") is of class: " + "'" + attributeClass.getName() + "' and is not a " +
323                 "descendent of BusinessObject.  Only descendents of BusinessObject " + "can be used.");
324         }
325 
326 		Map fkMap = new HashMap();
327 
328 		// make sure the attribute designated is listed as a
329 		// reference-descriptor on the clazz specified, otherwise
330 		// throw an exception (OJB);
331 		ClassDescriptor classDescriptor = getClassDescriptor(clazz);
332 		ObjectReferenceDescriptor referenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(attributeName);
333 		if (referenceDescriptor == null) {
334 			throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + attributeName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + clazz.getName() + "'");
335 		}
336 
337 		// special case when the attributeClass passed in doesnt match the
338 		// class of the reference-descriptor as defined in ojb-repository.
339 		// Currently
340 		// the only case of this happening is ObjectCode vs.
341 		// ObjectCodeCurrent.
342 		if (!attributeClass.equals(referenceDescriptor.getItemClass())) {
343 
344 			if (referenceConversionMap.containsKey(attributeClass)) {
345 				attributeClass = referenceConversionMap.get(attributeClass);
346 			} else {
347 				throw new RuntimeException("The Class of the Java member [" + attributeClass.getName() + "] '" + attributeName + "' does not match the class of the " + "reference-descriptor [" + referenceDescriptor.getItemClass().getName() + "]. " + "This is an unhandled special case for which special code needs to be written " + "in this class.");
348 			}
349 		}
350 
351 		// get the list of the foreign-keys for this reference-descriptor
352 		// (OJB)
353 		Vector fkFields = referenceDescriptor.getForeignKeyFields();
354 		Iterator fkIterator = fkFields.iterator();
355 
356 		// get the list of the corresponding pk fields on the other side of
357 		// the relationship
358 		List pkFields = getPrimaryKeys(attributeClass);
359 		Iterator pkIterator = pkFields.iterator();
360 
361 		// make sure the size of the pkIterator is the same as the
362 		// size of the fkIterator, otherwise this whole thing is borked
363 		if (pkFields.size() != fkFields.size()) {
364 			throw new RuntimeException("KualiPersistenceStructureService Error: The number of " + "foreign keys doesnt match the number of primary keys.  This may be a " + "result of misconfigured OJB-repository files.");
365 		}
366 
367 		// walk through the list of the foreign keys, get their types
368 		while (fkIterator.hasNext()) {
369 			// if there is a next FK but not a next PK, then we've got a big
370 			// problem,
371 			// and cannot continue
372 			if (!pkIterator.hasNext()) {
373 				throw new RuntimeException("The number of foriegn keys dont match the number of primary " + "keys for the reference '" + attributeName + "', on BO of type '" + clazz.getName() + "'.  " + "This should never happen under normal circumstances, as it means that the OJB repository " + "files are misconfigured.");
374 			}
375 
376 			// get the field name of the fk & pk field
377 			String fkFieldName = (String) fkIterator.next();
378 			String pkFieldName = (String) pkIterator.next();
379 
380 			// add the fieldName and fieldType to the map
381 			fkMap.put(fkFieldName, pkFieldName);
382 		}
383 
384 		return fkMap;
385 	}
386 
387 
388 	@Override
389 	public Map<String, String> getInverseForeignKeysForCollection(Class boClass, String collectionName) {
390 		// yelp if nulls were passed in
391 		if (boClass == null) {
392 			throw new IllegalArgumentException("The Class passed in for the boClass argument was null.");
393 		}
394 		if (collectionName == null) {
395 			throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
396 		}
397 
398 		PropertyDescriptor propertyDescriptor = null;
399 
400 		// make an instance of the class passed
401 		Object classInstance;
402 		try {
403 			classInstance = boClass.newInstance();
404 		} catch (Exception e) {
405 			throw new RuntimeException(e);
406 		}
407 
408 		// make sure the attribute exists at all, throw exception if not
409 		try {
410 			propertyDescriptor = PropertyUtils.getPropertyDescriptor(classInstance, collectionName);
411 		} catch (Exception e) {
412 			throw new RuntimeException(e);
413 		}
414 		if (propertyDescriptor == null) {
415 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + collectionName + "' does not exist " + "on class: '" + boClass.getName() + "'. GFK");
416 		}
417 
418 		// get the class of the attribute name
419 		Class attributeClass = propertyDescriptor.getPropertyType();
420 
421 		// make sure the class of the attribute descends from BusinessObject,
422 		// otherwise throw an exception
423 		if (!Collection.class.isAssignableFrom(attributeClass)) {
424 			throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + collectionName + ") is of class: " + "'" + attributeClass.getName() + "' and is not a " + "descendent of Collection");
425 		}
426 
427 		// make sure the collection designated is listed as a
428 		// collection-descriptor
429 		// on the boClass specified, otherwise throw an exception
430 		ClassDescriptor classDescriptor = getClassDescriptor(boClass);
431 		CollectionDescriptor collectionDescriptor = classDescriptor.getCollectionDescriptorByName(collectionName);
432 
433 		// in collections, the number of keys is equal to the number of keys in
434 		// the parent class (the class with the collection).
435 		// Each of the primary keys on the parent object will be mapped to a
436 		// field in the element object.
437 
438 		List parentForeignKeys = getPrimaryKeys(boClass);
439 		Vector childPrimaryKeysLegacy = collectionDescriptor.getForeignKeyFields();
440 
441 		if (parentForeignKeys.size() != childPrimaryKeysLegacy.size()) {
442 			throw new RuntimeException("The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
443 		}
444 
445 		Map<String, String> fkToPkMap = new HashMap<String, String>();
446 
447 		Iterator pFKIter = parentForeignKeys.iterator();
448 		Iterator cPKIterator = childPrimaryKeysLegacy.iterator();
449 
450 		while (pFKIter.hasNext()) {
451 			String parentForeignKey = (String) pFKIter.next();
452 			String childPrimaryKey = (String) cPKIterator.next();
453 
454 			fkToPkMap.put(parentForeignKey, childPrimaryKey);
455 		}
456 
457 		return fkToPkMap;
458 	}
459 
460 	/**
461 	 * @see org.kuali.rice.krad.service.PersistenceService#getNestedForeignKeyMap(java.lang.Class)
462 	 */
463 
464 	@Override
465 	public Map getNestedForeignKeyMap(Class persistableObjectClass) {
466 		Map fkMap = new HashMap();
467 		ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
468 		Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
469 		for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
470 			ObjectReferenceDescriptor objectReferenceDescriptor = (ObjectReferenceDescriptor) iter.next();
471 			ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());
472 
473 			FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
474 			FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
475 			for (int i = 0; i < pkFields.length; i++) {
476 				FieldDescriptor pkField = pkFields[i];
477 				fkMap.put(objectReferenceDescriptor.getAttributeName() + "." + pkField.getAttributeName(), fkFields[i].getAttributeName());
478 			}
479 		}
480 
481 		return fkMap;
482 	}
483 
484 	/**
485 	 * @see org.kuali.rice.krad.service.PersistenceMetadataService#hasPrimaryKeyFieldValues(java.lang.Object)
486 	 */
487 	@Override
488 	public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
489 		Map keyFields = getPrimaryKeyFieldValues(persistableObject);
490 
491 		boolean emptyField = false;
492 		for (Iterator i = keyFields.entrySet().iterator(); !emptyField && i.hasNext();) {
493 			Map.Entry e = (Map.Entry) i.next();
494 
495 			Object fieldValue = e.getValue();
496 			if (fieldValue == null) {
497 				emptyField = true;
498 			} else if (fieldValue instanceof String) {
499 				if (StringUtils.isEmpty((String) fieldValue)) {
500 					emptyField = true;
501 				} else {
502 					emptyField = false;
503 				}
504 			}
505 		}
506 
507 		return !emptyField;
508 	}
509 
510 	/**
511 	 * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldsPopulationState(org.kuali.rice.krad.bo.BusinessObject,
512 	 *      java.lang.String)
513 	 */
514 	@Override
515 	public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(PersistableBusinessObject bo, String referenceName) {
516 		boolean allFieldsPopulated = true;
517 		boolean anyFieldsPopulated = false;
518 		List<String> unpopulatedFields = new ArrayList<String>();
519 
520 		// yelp if nulls were passed in
521 		if (bo == null) {
522 			throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
523 		}
524 		if (StringUtils.isBlank(referenceName)) {
525 			throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty.");
526 		}
527 
528 		PropertyDescriptor propertyDescriptor = null;
529 
530 		// make sure the attribute exists at all, throw exception if not
531 		try {
532 			propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
533 		} catch (Exception e) {
534 			throw new RuntimeException(e);
535 		}
536 		if (propertyDescriptor == null) {
537 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
538 		}
539 
540 		// get the class of the attribute name
541 		Class referenceClass = propertyDescriptor.getPropertyType();
542 
543 		// make sure the class of the attribute descends from BusinessObject,
544 		// otherwise throw an exception
545 		if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
546 			throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + referenceName + ") is of class: " + "'" + referenceClass.getName() + "' and is not a " + "descendent of BusinessObject.  Only descendents of BusinessObject " + "can be used.");
547 		}
548 
549 		// make sure the attribute designated is listed as a
550 		// reference-descriptor
551 		// on the clazz specified, otherwise throw an exception (OJB);
552 
553 		ClassDescriptor classDescriptor = getClassDescriptor(bo.getClass());
554 
555 		// This block is a combination of legacy and jpa
556 		ObjectReferenceDescriptor referenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(referenceName);
557 		if (referenceDescriptor == null) {
558 			throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
559 		}
560 
561 		// get the list of the foreign-keys for this reference-descriptor
562 		Vector fkFieldsLegacy = referenceDescriptor.getForeignKeyFields();
563 		Iterator fkIteratorLegacy = fkFieldsLegacy.iterator();
564 
565 		// walk through the list of the foreign keys, get their types
566 		while (fkIteratorLegacy.hasNext()) {
567 
568 			// get the field name of the fk & pk field
569 			String fkFieldName = (String) fkIteratorLegacy.next();
570 
571 			// get the value for the fk field
572 			Object fkFieldValue = null;
573 			try {
574 				fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
575 			}
576 
577 			// abort if the value is not retrievable
578 			catch (Exception e) {
579 				throw new RuntimeException(e);
580 			}
581 
582 			// test the value
583 			if (fkFieldValue == null) {
584 				allFieldsPopulated = false;
585 				unpopulatedFields.add(fkFieldName);
586 			} else if (fkFieldValue instanceof String) {
587 				if (StringUtils.isBlank((String) fkFieldValue)) {
588 					allFieldsPopulated = false;
589 					unpopulatedFields.add(fkFieldName);
590 				} else {
591 					anyFieldsPopulated = true;
592 				}
593 			} else {
594 				anyFieldsPopulated = true;
595 			}
596 		}
597 
598 		// sanity check. if the flag for all fields populated is set, then
599 		// there should be nothing in the unpopulatedFields list
600 		if (allFieldsPopulated) {
601 			if (!unpopulatedFields.isEmpty()) {
602 				throw new RuntimeException("The flag is set that indicates all fields are populated, but there " + "are fields present in the unpopulatedFields list.  This should never happen, and indicates " + "that the logic in this method is broken.");
603 			}
604 		}
605 
606 		return new ForeignKeyFieldsPopulationState(allFieldsPopulated, anyFieldsPopulated, unpopulatedFields);
607 	}
608 
609 	/**
610 	 * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(java.lang.Class)
611 	 */
612 
613 	@Override
614 	public Map<String, Class> listReferenceObjectFields(Class boClass) {
615 		// validate parameter
616 		if (boClass == null) {
617 			throw new IllegalArgumentException("Class specified in the parameter was null.");
618 		}
619 
620 		Map<String, Class> references = new HashMap<String, Class>();
621 		ClassDescriptor classDescriptor = getClassDescriptor(boClass);
622 		Collection<ObjectReferenceDescriptor> referenceDescriptors = classDescriptor.getObjectReferenceDescriptors(true);
623 
624 		for (ObjectReferenceDescriptor referenceDescriptor : referenceDescriptors) {
625 			/*
626              * Below check is performed for OJB specific inheritance implementation. For more information see the OJB
627              * documentation: http://db.apache.org/ojb/docu/guides/advanced-technique.html#table-per-subclass
628              */
629             String superReferenceDescriptor = referenceDescriptor.getAttributeName();
630             if (!SuperReferenceDescriptor.SUPER_FIELD_INTERNAL_NAME.equals(superReferenceDescriptor)) {
631                 references.put(superReferenceDescriptor, referenceDescriptor.getItemClass());
632             }
633 		}
634 
635 		return references;
636 	}
637 
638 
639 	@Override
640 	public Map<String, Class> listCollectionObjectTypes(Class boClass) {
641 		if (boClass == null) {
642 			throw new IllegalArgumentException("Class specified in the parameter was null.");
643 		}
644 
645 		Map<String, Class> references = new HashMap<String, Class>();
646 		ClassDescriptor classDescriptor = null;
647 		try {
648 			classDescriptor = getClassDescriptor(boClass);
649 		} catch (ClassNotPersistableException cnpe) {
650 			return references;
651 		}
652 
653 		Collection<CollectionDescriptor> collectionDescriptors = classDescriptor.getCollectionDescriptors(true);
654 		for (CollectionDescriptor collectionDescriptor : collectionDescriptors) {
655 			references.put(collectionDescriptor.getAttributeName(), collectionDescriptor.getItemClass());
656 		}
657 
658 		return references;
659 	}
660 
661 	@Override
662 	public Map<String, Class> listCollectionObjectTypes(PersistableBusinessObject bo) {
663 		// validate parameter
664 		if (bo == null) {
665 			throw new IllegalArgumentException("BO specified in the parameter was null.");
666 		}
667 		if (!(bo instanceof PersistableBusinessObject)) {
668 			throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
669 		}
670 
671 		return listCollectionObjectTypes(bo.getClass());
672 	}
673 
674 	/**
675 	 * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(org.kuali.rice.krad.bo.BusinessObject)
676 	 */
677 	@Override
678 	public Map<String, Class> listReferenceObjectFields(PersistableBusinessObject bo) {
679 		// validate parameter
680 		if (bo == null) {
681 			throw new IllegalArgumentException("BO specified in the parameter was null.");
682 		}
683 
684 		return listReferenceObjectFields(bo.getClass());
685 	}
686 
687 
688 	@Override
689 	public boolean isReferenceUpdatable(Class boClass, String referenceName) {
690 		ClassDescriptor classDescriptor = getClassDescriptor(boClass);
691 		ObjectReferenceDescriptor refDesc = classDescriptor.getObjectReferenceDescriptorByName(referenceName);
692 		return refDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
693 	}
694 
695 
696 	@Override
697 	public boolean isCollectionUpdatable(Class boClass, String collectionName) {
698 		ClassDescriptor cd = getClassDescriptor(boClass);
699 		CollectionDescriptor collDesc = cd.getCollectionDescriptorByName(collectionName);
700 		return collDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
701 	}
702 
703 
704 	@Override
705 	public boolean hasCollection(Class boClass, String collectionName) {
706 		ClassDescriptor cd = getClassDescriptor(boClass);
707 		return cd.getCollectionDescriptorByName(collectionName) != null;
708 	}
709 
710 
711 	@Override
712 	public boolean hasReference(Class boClass, String referenceName) {
713 		ClassDescriptor cd = getClassDescriptor(boClass);
714 		return cd.getObjectReferenceDescriptorByName(referenceName) != null;
715 	}
716 
717 	/**
718 	 * This overridden method ...
719 	 *
720 	 * @see org.kuali.rice.krad.service.PersistenceStructureService#getTableName(java.lang.Class)
721 	 */
722 
723 	@Override
724 	public String getTableName(Class<? extends PersistableBusinessObject> boClass) {
725 		ClassDescriptor cd = getClassDescriptor(boClass);
726 		return cd.getFullTableName();
727 	}
728 
729 
730 }
731