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