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