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