1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.kns.datadictionary;
18
19 import java.beans.IntrospectionException;
20 import java.beans.PropertyDescriptor;
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeMap;
32
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.kuali.rice.core.util.ClassLoaderUtils;
37 import org.kuali.rice.kns.bo.BusinessObject;
38 import org.kuali.rice.kns.bo.PersistableBusinessObjectExtension;
39 import org.kuali.rice.kns.datadictionary.exception.AttributeValidationException;
40 import org.kuali.rice.kns.datadictionary.exception.CompletionException;
41 import org.kuali.rice.kns.service.KNSServiceLocator;
42 import org.kuali.rice.kns.service.ModuleService;
43 import org.kuali.rice.kns.service.PersistenceStructureService;
44 import org.kuali.rice.kns.util.ObjectUtils;
45 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
46 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
47 import org.springframework.core.io.DefaultResourceLoader;
48 import org.springframework.core.io.Resource;
49
50
51
52
53
54
55
56
57 public class DataDictionary {
58
59 private DefaultListableBeanFactory ddBeans = new DefaultListableBeanFactory();
60 private XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ddBeans);
61
62
63 private static final Log LOG = LogFactory.getLog(DataDictionary.class);
64
65
66
67
68 private DataDictionaryIndex ddIndex = new DataDictionaryIndex(ddBeans);
69
70
71
72
73
74
75 private DataDictionaryMapper ddMapper = new DataDictionaryIndexMapper();
76
77 private List<String> configFileLocations = new ArrayList<String>();
78
79 public DataDictionary() { }
80
81 public List<String> getConfigFileLocations() {
82 return this.configFileLocations;
83 }
84
85 public void setConfigFileLocations(List<String> configFileLocations) {
86 this.configFileLocations = configFileLocations;
87 }
88
89 public void addConfigFileLocation( String location ) throws IOException {
90 indexSource( location );
91 }
92
93
94
95
96
97 public void setDataDictionaryMapper(DataDictionaryMapper mapper) {
98 this.ddMapper = mapper;
99 }
100
101 private void indexSource(String sourceName) throws IOException {
102 if (sourceName == null) {
103 throw new DataDictionaryException("Source Name given is null");
104 }
105
106 if (!sourceName.endsWith(".xml") ) {
107 Resource resource = getFileResource(sourceName);
108 if (resource.exists()) {
109 indexSource(resource.getFile());
110 } else {
111 LOG.warn("Could not find " + sourceName);
112 throw new DataDictionaryException("DD Resource " + sourceName + " not found");
113 }
114 } else {
115 if ( LOG.isDebugEnabled() ) {
116 LOG.debug("adding sourceName " + sourceName + " ");
117 }
118 Resource resource = getFileResource(sourceName);
119 if (! resource.exists()) {
120 throw new DataDictionaryException("DD Resource " + sourceName + " not found");
121 }
122 String indexName = sourceName.substring(sourceName.lastIndexOf("/") + 1, sourceName.indexOf(".xml"));
123 configFileLocations.add( sourceName );
124 }
125 }
126
127 private Resource getFileResource(String sourceName) {
128 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
129 return resourceLoader.getResource(sourceName);
130 }
131
132 private void indexSource(File dir) {
133 for (File file : dir.listFiles()) {
134 if (file.isDirectory()) {
135 indexSource(file);
136 } else if (file.getName().endsWith(".xml") ) {
137 configFileLocations.add( "file:" + file.getAbsolutePath());
138 } else {
139 if ( LOG.isDebugEnabled() ) {
140 LOG.debug("Skipping non xml file " + file.getAbsolutePath() + " in DD load");
141 }
142 }
143 }
144 }
145
146 public void parseDataDictionaryConfigurationFiles( boolean allowConcurrentValidation ) {
147
148
149 LOG.info( "Starting DD XML File Load" );
150 String[] configFileLocationsArray = new String[configFileLocations.size()];
151 configFileLocationsArray = configFileLocations.toArray( configFileLocationsArray );
152 configFileLocations.clear();
153 try {
154 xmlReader.loadBeanDefinitions( configFileLocationsArray );
155 } catch (Exception e) {
156 LOG.error("Error loading bean definitions", e);
157 throw new DataDictionaryException("Error loading bean definitions: " + e.getLocalizedMessage());
158 }
159 LOG.info( "Completed DD XML File Load" );
160 if ( allowConcurrentValidation ) {
161 Thread t = new Thread(ddIndex);
162 t.start();
163 } else {
164 ddIndex.run();
165 }
166 }
167
168 static boolean validateEBOs = true;
169
170 public void validateDD( boolean validateEbos ) {
171 DataDictionary.validateEBOs = validateEbos;
172 Map<String,BusinessObjectEntry> boBeans = ddBeans.getBeansOfType(BusinessObjectEntry.class);
173 for ( BusinessObjectEntry entry : boBeans.values() ) {
174 entry.completeValidation();
175 }
176 Map<String,DocumentEntry> docBeans = ddBeans.getBeansOfType(DocumentEntry.class);
177 for ( DocumentEntry entry : docBeans.values() ) {
178 entry.completeValidation();
179 }
180 }
181
182 public void validateDD() {
183 DataDictionary.validateEBOs = true;
184 Map<String,BusinessObjectEntry> boBeans = ddBeans.getBeansOfType(BusinessObjectEntry.class);
185 for ( BusinessObjectEntry entry : boBeans.values() ) {
186 entry.completeValidation();
187 }
188 Map<String,DocumentEntry> docBeans = ddBeans.getBeansOfType(DocumentEntry.class);
189 for ( DocumentEntry entry : docBeans.values() ) {
190 entry.completeValidation();
191 }
192 }
193
194
195
196
197
198 public BusinessObjectEntry getBusinessObjectEntry(String className ) {
199 return ddMapper.getBusinessObjectEntry(ddIndex, className);
200 }
201
202
203
204
205
206
207
208 public BusinessObjectEntry getBusinessObjectEntryForConcreteClass(String className){
209 return ddMapper.getBusinessObjectEntryForConcreteClass(ddIndex, className);
210 }
211
212
213
214
215 public List<String> getBusinessObjectClassNames() {
216 return ddMapper.getBusinessObjectClassNames(ddIndex);
217 }
218
219
220
221
222 public Map<String, BusinessObjectEntry> getBusinessObjectEntries() {
223 return ddMapper.getBusinessObjectEntries(ddIndex);
224 }
225
226
227
228
229
230
231 public DataDictionaryEntry getDictionaryObjectEntry(String className) {
232 return ddMapper.getDictionaryObjectEntry(ddIndex, className);
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 public DocumentEntry getDocumentEntry(String documentTypeDDKey ) {
251 return ddMapper.getDocumentEntry(ddIndex, documentTypeDDKey);
252 }
253
254
255
256
257
258
259
260
261
262
263
264 public MaintenanceDocumentEntry getMaintenanceDocumentEntryForBusinessObjectClass(Class businessObjectClass) {
265 return ddMapper.getMaintenanceDocumentEntryForBusinessObjectClass(ddIndex, businessObjectClass);
266 }
267
268 public Map<String, DocumentEntry> getDocumentEntries() {
269 return ddMapper.getDocumentEntries(ddIndex);
270 }
271
272
273
274
275
276
277
278 public static boolean isPropertyOf(Class targetClass, String propertyName) {
279 if (targetClass == null) {
280 throw new IllegalArgumentException("invalid (null) targetClass");
281 }
282 if (StringUtils.isBlank(propertyName)) {
283 throw new IllegalArgumentException("invalid (blank) propertyName");
284 }
285
286 PropertyDescriptor propertyDescriptor = buildReadDescriptor(targetClass, propertyName);
287
288 boolean isPropertyOf = (propertyDescriptor != null);
289 return isPropertyOf;
290 }
291
292
293
294
295
296
297
298 public static boolean isCollectionPropertyOf(Class targetClass, String propertyName) {
299 boolean isCollectionPropertyOf = false;
300
301 PropertyDescriptor propertyDescriptor = buildReadDescriptor(targetClass, propertyName);
302 if (propertyDescriptor != null) {
303 Class clazz = propertyDescriptor.getPropertyType();
304
305 if ((clazz != null) && Collection.class.isAssignableFrom(clazz)) {
306 isCollectionPropertyOf = true;
307 }
308 }
309
310 return isCollectionPropertyOf;
311 }
312
313 public static PersistenceStructureService persistenceStructureService;
314
315
316
317
318 public static PersistenceStructureService getPersistenceStructureService() {
319 if ( persistenceStructureService == null ) {
320 persistenceStructureService = KNSServiceLocator.getPersistenceStructureService();
321 }
322 return persistenceStructureService;
323 }
324
325
326
327
328
329
330
331
332
333 public static Class getAttributeClass(Class boClass, String attributeName) {
334
335
336 if (!isPropertyOf(boClass, attributeName)) {
337 throw new AttributeValidationException("unable to find attribute '" + attributeName + "' in rootClass '" + boClass.getName() + "'");
338 }
339
340
341
342
343 if(boClass.isInterface())
344 return getAttributeClassWhenBOIsInterface(boClass, attributeName);
345 else
346 return getAttributeClassWhenBOIsClass(boClass, attributeName);
347
348 }
349
350
351
352
353
354
355
356
357
358 private static Class getAttributeClassWhenBOIsClass(Class boClass, String attributeName){
359 BusinessObject boInstance;
360 try {
361 boInstance = (BusinessObject) boClass.newInstance();
362 } catch (Exception e) {
363 throw new RuntimeException("Unable to instantiate BO: " + boClass, e);
364 }
365
366
367 try {
368 return ObjectUtils.getPropertyType(boInstance, attributeName, getPersistenceStructureService());
369 } catch (Exception e) {
370 throw new RuntimeException("Unable to determine property type for: " + boClass.getName() + "." + attributeName, e);
371 }
372 }
373
374
375
376
377
378
379
380
381
382
383
384 private static Class getAttributeClassWhenBOIsInterface(Class boClass, String attributeName){
385 if (boClass == null) {
386 throw new IllegalArgumentException("invalid (null) boClass");
387 }
388 if (StringUtils.isBlank(attributeName)) {
389 throw new IllegalArgumentException("invalid (blank) attributeName");
390 }
391
392 PropertyDescriptor propertyDescriptor = null;
393
394 String[] intermediateProperties = attributeName.split("\\.");
395 int lastLevel = intermediateProperties.length - 1;
396 Class currentClass = boClass;
397
398 for (int i = 0; i <= lastLevel; ++i) {
399
400 String currentPropertyName = intermediateProperties[i];
401 propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
402
403 if (propertyDescriptor != null) {
404
405 Class propertyType = propertyDescriptor.getPropertyType();
406 if ( propertyType.equals( PersistableBusinessObjectExtension.class ) ) {
407 propertyType = getPersistenceStructureService().getBusinessObjectAttributeClass( currentClass, currentPropertyName );
408 }
409 if (Collection.class.isAssignableFrom(propertyType)) {
410
411 throw new AttributeValidationException("Can't determine the Class of Collection elements because when the business object is an (possibly ExternalizableBusinessObject) interface.");
412 }
413 else {
414 currentClass = propertyType;
415 }
416 }
417 else {
418 throw new AttributeValidationException("Can't find getter method of " + boClass.getName() + " for property " + attributeName);
419 }
420 }
421 return currentClass;
422 }
423
424
425
426
427
428
429
430
431 public static Class getCollectionElementClass(Class boClass, String collectionName) {
432 if (boClass == null) {
433 throw new IllegalArgumentException("invalid (null) boClass");
434 }
435 if (StringUtils.isBlank(collectionName)) {
436 throw new IllegalArgumentException("invalid (blank) collectionName");
437 }
438
439 PropertyDescriptor propertyDescriptor = null;
440
441 String[] intermediateProperties = collectionName.split("\\.");
442 Class currentClass = boClass;
443
444 for (int i = 0; i <intermediateProperties.length; ++i) {
445
446 String currentPropertyName = intermediateProperties[i];
447 propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
448
449
450 if (propertyDescriptor != null) {
451
452 Class type = propertyDescriptor.getPropertyType();
453 if (Collection.class.isAssignableFrom(type)) {
454
455 if (getPersistenceStructureService().isPersistable(currentClass)) {
456
457 Map<String, Class> collectionClasses = new HashMap<String, Class>();
458 collectionClasses = getPersistenceStructureService().listCollectionObjectTypes(currentClass);
459 currentClass = collectionClasses.get(currentPropertyName);
460
461 }
462 else {
463 throw new RuntimeException("Can't determine the Class of Collection elements because persistenceStructureService.isPersistable(" + currentClass.getName() + ") returns false.");
464 }
465
466 }
467 else {
468
469 currentClass = propertyDescriptor.getPropertyType();
470
471 }
472 }
473 }
474
475 return currentClass;
476 }
477
478 static private Map<String, Map<String, PropertyDescriptor>> cache = new TreeMap<String, Map<String, PropertyDescriptor>>();
479
480
481
482
483
484
485 public static PropertyDescriptor buildReadDescriptor(Class propertyClass, String propertyName) {
486 if (propertyClass == null) {
487 throw new IllegalArgumentException("invalid (null) propertyClass");
488 }
489 if (StringUtils.isBlank(propertyName)) {
490 throw new IllegalArgumentException("invalid (blank) propertyName");
491 }
492
493 PropertyDescriptor propertyDescriptor = null;
494
495 String[] intermediateProperties = propertyName.split("\\.");
496 int lastLevel = intermediateProperties.length - 1;
497 Class currentClass = propertyClass;
498
499 for (int i = 0; i <= lastLevel; ++i) {
500
501 String currentPropertyName = intermediateProperties[i];
502 propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
503
504 if (i < lastLevel) {
505
506 if (propertyDescriptor != null) {
507
508 Class propertyType = propertyDescriptor.getPropertyType();
509 if ( propertyType.equals( PersistableBusinessObjectExtension.class ) ) {
510 propertyType = getPersistenceStructureService().getBusinessObjectAttributeClass( currentClass, currentPropertyName );
511 }
512 if (Collection.class.isAssignableFrom(propertyType)) {
513
514 if (getPersistenceStructureService().isPersistable(currentClass)) {
515
516 Map<String, Class> collectionClasses = new HashMap<String, Class>();
517 collectionClasses = getPersistenceStructureService().listCollectionObjectTypes(currentClass);
518 currentClass = collectionClasses.get(currentPropertyName);
519
520 }
521 else {
522
523 throw new RuntimeException("Can't determine the Class of Collection elements because persistenceStructureService.isPersistable(" + currentClass.getName() + ") returns false.");
524
525 }
526
527 }
528 else {
529
530 currentClass = propertyType;
531
532 }
533
534 }
535
536 }
537
538 }
539
540 return propertyDescriptor;
541 }
542
543
544
545
546
547
548 public static PropertyDescriptor buildSimpleReadDescriptor(Class propertyClass, String propertyName) {
549 if (propertyClass == null) {
550 throw new IllegalArgumentException("invalid (null) propertyClass");
551 }
552 if (StringUtils.isBlank(propertyName)) {
553 throw new IllegalArgumentException("invalid (blank) propertyName");
554 }
555
556 PropertyDescriptor p = null;
557
558
559 String propertyClassName = propertyClass.getName();
560 Map<String, PropertyDescriptor> m = cache.get(propertyClassName);
561 if (null != m) {
562 p = m.get(propertyName);
563 if (null != p) {
564 return p;
565 }
566 }
567
568 String prefix = StringUtils.capitalize(propertyName);
569 String getName = "get" + prefix;
570 String isName = "is" + prefix;
571
572 try {
573
574 p = new PropertyDescriptor(propertyName, propertyClass, getName, null);
575
576 }
577 catch (IntrospectionException e) {
578 try {
579
580 p = new PropertyDescriptor(propertyName, propertyClass, isName, null);
581
582 }
583 catch (IntrospectionException f) {
584
585 }
586 }
587
588
589 if (null != p) {
590
591 if (null == m) {
592
593 m = new TreeMap<String, PropertyDescriptor>();
594 cache.put(propertyClassName, m);
595
596 }
597
598 m.put(propertyName, p);
599
600 }
601
602 return p;
603 }
604
605 public Set<InactivationBlockingMetadata> getAllInactivationBlockingMetadatas(Class blockedClass) {
606 return ddMapper.getAllInactivationBlockingMetadatas(ddIndex, blockedClass);
607 }
608
609
610
611
612
613 public void performBeanOverrides()
614 {
615 Collection<BeanOverride> beanOverrides = ddBeans.getBeansOfType(BeanOverride.class).values();
616
617 if (beanOverrides.isEmpty()){
618 LOG.info("DataDictionary.performOverrides(): No beans to override");
619 }
620 for (BeanOverride beanOverride : beanOverrides) {
621
622 Object bean = ddBeans.getBean(beanOverride.getBeanName());
623 beanOverride.performOverride(bean);
624 LOG.info("DataDictionary.performOverrides(): Performing override on bean: " + bean.toString());
625 }
626 }
627 }