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