View Javadoc

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