1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.service.impl;
17
18 import java.beans.PropertyDescriptor;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.beanutils.PropertyUtils;
28 import org.apache.commons.lang.StringUtils;
29 import org.kuali.rice.core.framework.persistence.jpa.metadata.EntityDescriptor;
30 import org.kuali.rice.core.framework.persistence.jpa.metadata.JoinColumnDescriptor;
31 import org.kuali.rice.core.framework.persistence.jpa.metadata.MetadataManager;
32 import org.kuali.rice.core.framework.persistence.jpa.metadata.ObjectDescriptor;
33 import org.kuali.rice.krad.bo.DataObjectRelationship;
34 import org.kuali.rice.krad.bo.PersistableBusinessObject;
35 import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
36 import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
37 import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
38 import org.kuali.rice.krad.service.PersistenceStructureService;
39 import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState;
40
41 public class PersistenceStructureServiceJpaImpl extends PersistenceServiceImplBase implements PersistenceStructureService {
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();
59
60
61
62
63
64 public boolean isPersistable(Class clazz) {
65 boolean isPersistable = false;
66
67 if (MetadataManager.getEntityDescriptor(clazz) != null) {
68 isPersistable = true;
69 }
70
71 return isPersistable;
72 }
73
74
75
76
77
78 public List getPrimaryKeys(Class clazz) {
79 List pkList = new ArrayList();
80
81 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(clazz);
82 for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getPrimaryKeys()) {
83 pkList.add(field.getName());
84 }
85
86 return pkList;
87 }
88
89
90
91
92
93 public List listFieldNames(Class clazz) {
94 List fieldNames = new ArrayList();
95
96 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(clazz);
97 for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getFields()) {
98 fieldNames.add(field.getName());
99 }
100
101 return fieldNames;
102 }
103
104
105
106
107
108
109 public Object clearPrimaryKeyFields(Object persistableObject) {
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 return persistableObject;
132 }
133
134
135
136
137
138
139
140 public List listPersistableSubclasses(Class superclazz) {
141 List persistableSubclasses = new ArrayList();
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 return persistableSubclasses;
157 }
158
159
160
161
162
163
164 public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName, String attributePrefix) {
165 if (persistableClass == null) {
166 throw new IllegalArgumentException("invalid (null) persistableClass");
167 }
168 if (StringUtils.isBlank(attributeName)) {
169 throw new IllegalArgumentException("invalid (blank) attributeName");
170 }
171
172 Map<String, DataObjectRelationship> relationships = new HashMap<String, DataObjectRelationship>();
173
174 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableClass);
175 for (ObjectDescriptor objectDescriptor : descriptor.getObjectRelationships()) {
176 List<String> fks = objectDescriptor.getForeignKeyFields();
177 if (fks.contains(attributeName) || objectDescriptor.getAttributeName().equals(attributeName)) {
178 Map<String, String> fkToPkRefs = getForeignKeysForReference(persistableClass, objectDescriptor.getAttributeName());
179 DataObjectRelationship
180 rel = new DataObjectRelationship(persistableClass, objectDescriptor.getAttributeName(), objectDescriptor.getTargetEntity());
181 for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
182 if (StringUtils.isBlank(attributePrefix)) {
183 rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
184 } else {
185 rel.getParentToChildReferences().put(attributePrefix + "." + ref.getKey(), ref.getValue());
186 }
187 }
188 relationships.put(objectDescriptor.getAttributeName(), rel);
189 }
190 }
191
192 return relationships;
193 }
194
195
196
197 public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName) {
198 return getRelationshipMetadata(persistableClass, attributeName, null);
199 }
200
201
202
203
204
205
206 public String getForeignKeyFieldName(Class persistableObjectClass, String attributeName, String pkName) {
207 String fkName = null;
208
209 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
210 ObjectDescriptor objectDescriptor = descriptor.getObjectDescriptorByName(attributeName);
211 if (objectDescriptor == null) {
212 throw new RuntimeException("Attribute name " + attributeName + " is not a valid reference to class " + persistableObjectClass.getName());
213 }
214 List<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor> matches = new ArrayList<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor>();
215 for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getFields()) {
216 String column = field.getColumn();
217 for (JoinColumnDescriptor join : objectDescriptor.getJoinColumnDescriptors()) {
218 if (column != null && column.equals(join.getName())) {
219 matches.add(field);
220 }
221 }
222 }
223
224 if (matches.size() == 1) {
225 fkName = matches.get(0).getName();
226 } else {
227 throw new RuntimeException("Implement me!");
228 }
229
230 return fkName;
231 }
232
233
234
235
236
237
238 public Map getReferencesForForeignKey(Class persistableObjectClass, String attributeName) {
239 Map referenceClasses = new HashMap();
240
241 if (PersistableBusinessObject.class.isAssignableFrom(persistableObjectClass)) {
242 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
243 for (ObjectDescriptor objectDescriptor : descriptor.getObjectRelationships()) {
244 List<String> refFkNames = objectDescriptor.getForeignKeyFields();
245 for (String fk : refFkNames) {
246 if (fk.equals(attributeName)) {
247 referenceClasses.put(objectDescriptor.getAttributeName(), objectDescriptor.getTargetEntity());
248 }
249 }
250 }
251 }
252
253 return referenceClasses;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 public Map getForeignKeysForReference(Class clazz, String attributeName) {
274
275
276 if (clazz == null) {
277 throw new IllegalArgumentException("The Class passed in for the clazz argument was null.");
278 }
279 if (attributeName == null) {
280 throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
281 }
282
283
284 Class attributeClass = getBusinessObjectAttributeClass(clazz, attributeName);
285 if (attributeClass == null) {
286 throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + attributeName + "' does not exist on class: '" + clazz.getName() + "'.");
287 }
288
289
290
291 if (!PersistableBusinessObject.class.isAssignableFrom(attributeClass)) {
292 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.");
293 }
294
295 return determineFkMap(clazz, attributeName, attributeClass);
296 }
297
298 private Map determineFkMap(Class clazz, String attributeName, Class attributeClass) {
299 Map fkMap = new HashMap();
300 EntityDescriptor entityDescriptor = MetadataManager.getEntityDescriptor(clazz);
301 ObjectDescriptor objectDescriptor = entityDescriptor.getObjectDescriptorByName(attributeName);
302 if (objectDescriptor == null) {
303 throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + attributeName + ") is not defined in JPA annotations for class: '" + clazz.getName() + "'");
304 }
305
306
307
308
309
310
311 if (!attributeClass.equals(objectDescriptor.getTargetEntity())) {
312 if (referenceConversionMap.containsKey(attributeClass)) {
313 attributeClass = referenceConversionMap.get(attributeClass);
314 } else {
315 throw new RuntimeException("The Class of the Java member [" + attributeClass.getName() + "] '" + attributeName + "' does not match the class of the reference [" + objectDescriptor.getTargetEntity().getName() + "]. " + "This is an unhandled special case for which special code needs to be written in this class.");
316 }
317 }
318
319
320
321 List<String> fkFields = objectDescriptor.getForeignKeyFields();
322 Iterator<String> fkIterator = fkFields.iterator();
323
324
325
326 List pkFields = getPrimaryKeys(attributeClass);
327 Iterator pkIterator = pkFields.iterator();
328
329
330
331 if (pkFields.size() != fkFields.size()) {
332 throw new RuntimeException("KualiPersistenceStructureService Error: The number of " + "foreign keys doesnt match the number of primary keys.");
333 }
334
335
336 while (fkIterator.hasNext()) {
337
338
339
340 if (!pkIterator.hasNext()) {
341 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.");
342 }
343
344
345 String fkFieldName = (String) fkIterator.next();
346 String pkFieldName = (String) pkIterator.next();
347
348
349 fkMap.put(fkFieldName, pkFieldName);
350 }
351 return fkMap;
352 }
353
354
355 public Map<String, String> getInverseForeignKeysForCollection(Class boClass, String collectionName) {
356
357 if (boClass == null) {
358 throw new IllegalArgumentException("The Class passed in for the boClass argument was null.");
359 }
360 if (collectionName == null) {
361 throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
362 }
363
364 PropertyDescriptor propertyDescriptor = null;
365
366
367 Object classInstance;
368 try {
369 classInstance = boClass.newInstance();
370 } catch (Exception e) {
371 throw new RuntimeException(e);
372 }
373
374
375 try {
376 propertyDescriptor = PropertyUtils.getPropertyDescriptor(classInstance, collectionName);
377 } catch (Exception e) {
378 throw new RuntimeException(e);
379 }
380 if (propertyDescriptor == null) {
381 throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + collectionName + "' does not exist " + "on class: '" + boClass.getName() + "'. GFK");
382 }
383
384
385 Class attributeClass = propertyDescriptor.getPropertyType();
386
387
388
389 if (!Collection.class.isAssignableFrom(attributeClass)) {
390 throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + collectionName + ") is of class: " + "'" + attributeClass.getName() + "' and is not a " + "descendent of Collection");
391 }
392
393 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
394 org.kuali.rice.core.framework.persistence.jpa.metadata.CollectionDescriptor cd = descriptor.getCollectionDescriptorByName(collectionName);
395 List<String> childPrimaryKeys = cd.getForeignKeyFields();
396
397 List parentForeignKeys = getPrimaryKeys(boClass);
398
399 if (parentForeignKeys.size() != childPrimaryKeys.size()) {
400 throw new RuntimeException("The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
401 }
402
403 Map<String, String> fkToPkMap = new HashMap<String, String>();
404 Iterator pFKIter = parentForeignKeys.iterator();
405 Iterator cPKIterator = childPrimaryKeys.iterator();
406
407 while (pFKIter.hasNext()) {
408 String parentForeignKey = (String) pFKIter.next();
409 String childPrimaryKey = (String) cPKIterator.next();
410
411 fkToPkMap.put(parentForeignKey, childPrimaryKey);
412 }
413 return fkToPkMap;
414 }
415
416
417
418
419
420 public Map getNestedForeignKeyMap(Class persistableObjectClass) {
421 Map fkMap = new HashMap();
422
423
424
425
426 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
427 for (ObjectDescriptor objectReferenceDescriptor : descriptor.getObjectRelationships()) {
428 EntityDescriptor referenceDescriptor = MetadataManager.getEntityDescriptor(objectReferenceDescriptor.getTargetEntity());
429 List<String> fkFields = objectReferenceDescriptor.getForeignKeyFields();
430 Set<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor> pkFields = referenceDescriptor.getPrimaryKeys();
431 int i = 0;
432 for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor fd : pkFields) {
433 fkMap.put(objectReferenceDescriptor.getAttributeName() + "." + fd.getName(), fkFields.get(i));
434 i++;
435 }
436 }
437
438 return fkMap;
439 }
440
441
442
443
444 public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
445 Map keyFields = getPrimaryKeyFieldValues(persistableObject);
446
447 boolean emptyField = false;
448 for (Iterator i = keyFields.entrySet().iterator(); !emptyField && i.hasNext();) {
449 Map.Entry e = (Map.Entry) i.next();
450
451 Object fieldValue = e.getValue();
452 if (fieldValue == null) {
453 emptyField = true;
454 } else if (fieldValue instanceof String) {
455 if (StringUtils.isEmpty((String) fieldValue)) {
456 emptyField = true;
457 } else {
458 emptyField = false;
459 }
460 }
461 }
462
463 return !emptyField;
464 }
465
466
467
468
469
470 public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(PersistableBusinessObject bo, String referenceName) {
471
472 boolean allFieldsPopulated = true;
473 boolean anyFieldsPopulated = false;
474 List<String> unpopulatedFields = new ArrayList<String>();
475
476
477 if (bo == null) {
478 throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
479 }
480 if (StringUtils.isBlank(referenceName)) {
481 throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty.");
482 }
483
484 PropertyDescriptor propertyDescriptor = null;
485
486
487 try {
488 propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
489 } catch (Exception e) {
490 throw new RuntimeException(e);
491 }
492 if (propertyDescriptor == null) {
493 throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
494 }
495
496
497 Class referenceClass = propertyDescriptor.getPropertyType();
498
499
500
501 if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
502 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.");
503 }
504
505 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(bo.getClass());
506 ObjectDescriptor objectDescriptor = descriptor.getObjectDescriptorByName(referenceName);
507
508 if (objectDescriptor == null) {
509 throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
510 }
511
512 List<String> fkFields = objectDescriptor.getForeignKeyFields();
513 Iterator fkIterator = fkFields.iterator();
514
515
516 while (fkIterator.hasNext()) {
517
518
519 String fkFieldName = (String) fkIterator.next();
520
521
522 Object fkFieldValue = null;
523 try {
524 fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
525 }
526
527
528 catch (Exception e) {
529 throw new RuntimeException(e);
530 }
531
532
533 if (fkFieldValue == null) {
534 allFieldsPopulated = false;
535 unpopulatedFields.add(fkFieldName);
536 } else if (fkFieldValue instanceof String) {
537 if (StringUtils.isBlank((String) fkFieldValue)) {
538 allFieldsPopulated = false;
539 unpopulatedFields.add(fkFieldName);
540 } else {
541 anyFieldsPopulated = true;
542 }
543 } else {
544 anyFieldsPopulated = true;
545 }
546 }
547
548
549
550 if (allFieldsPopulated) {
551 if (!unpopulatedFields.isEmpty()) {
552 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.");
553 }
554 }
555
556 return new ForeignKeyFieldsPopulationState(allFieldsPopulated, anyFieldsPopulated, unpopulatedFields);
557 }
558
559
560
561
562
563 public Map<String, Class> listReferenceObjectFields(Class boClass) {
564
565 if (boClass == null) {
566 throw new IllegalArgumentException("Class specified in the parameter was null.");
567 }
568 if (!PersistableBusinessObject.class.isAssignableFrom(boClass)) {
569 throw new IllegalArgumentException("Class specified [" + boClass.getName() + "] must be a class that " + "inherits from BusinessObject.");
570 }
571
572 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
573 Map<String, Class> references = new HashMap();
574 for (org.kuali.rice.core.framework.persistence.jpa.metadata.ObjectDescriptor od : descriptor.getObjectRelationships()) {
575 references.put(od.getAttributeName(), od.getTargetEntity());
576 }
577
578 return references;
579 }
580
581
582 public Map<String, Class> listCollectionObjectTypes(Class boClass) {
583 if (boClass == null) {
584 throw new IllegalArgumentException("Class specified in the parameter was null.");
585 }
586
587 Map<String, Class> references = new HashMap();
588
589 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
590 if (descriptor == null) {
591 return references;
592 }
593
594 for (org.kuali.rice.core.framework.persistence.jpa.metadata.CollectionDescriptor cd : descriptor.getCollectionRelationships()) {
595 references.put(cd.getAttributeName(), cd.getTargetEntity());
596 }
597
598 return references;
599 }
600
601 public Map<String, Class> listCollectionObjectTypes(PersistableBusinessObject bo) {
602
603 if (bo == null) {
604 throw new IllegalArgumentException("BO specified in the parameter was null.");
605 }
606 if (!(bo instanceof PersistableBusinessObject)) {
607 throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
608 }
609
610 return listCollectionObjectTypes(bo.getClass());
611 }
612
613
614
615
616 public Map<String, Class> listReferenceObjectFields(PersistableBusinessObject bo) {
617
618 if (bo == null) {
619 throw new IllegalArgumentException("BO specified in the parameter was null.");
620 }
621 if (!(bo instanceof PersistableBusinessObject)) {
622 throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
623 }
624
625 return listReferenceObjectFields(bo.getClass());
626 }
627
628
629 public boolean isReferenceUpdatable(Class boClass, String referenceName) {
630 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
631 return descriptor.getObjectDescriptorByName(referenceName).isUpdateable();
632
633
634
635
636
637
638 }
639
640
641 public boolean isCollectionUpdatable(Class boClass, String collectionName) {
642 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
643 return descriptor.getCollectionDescriptorByName(collectionName).isUpdateable();
644
645
646
647
648
649
650 }
651
652
653 public boolean hasCollection(Class boClass, String collectionName) {
654 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
655 return descriptor.getCollectionDescriptorByName(collectionName) != null;
656 }
657
658
659 public boolean hasReference(Class boClass, String referenceName) {
660 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
661 return descriptor.getObjectDescriptorByName(referenceName) != null;
662 }
663
664
665
666
667
668 public String getTableName(Class<? extends PersistableBusinessObject> boClass) {
669 EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
670 return descriptor.getTable();
671 }
672 }
673