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.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.kuali.rice.core.api.util.ClassLoaderUtils;
22  import org.kuali.rice.krad.bo.BusinessObject;
23  import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension;
24  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
25  import org.kuali.rice.krad.datadictionary.exception.CompletionException;
26  import org.kuali.rice.krad.datadictionary.parse.StringListConverter;
27  import org.kuali.rice.krad.datadictionary.parse.StringMapConverter;
28  import org.kuali.rice.krad.service.KRADServiceLocator;
29  import org.kuali.rice.krad.service.PersistenceStructureService;
30  import org.kuali.rice.krad.uif.view.View;
31  import org.kuali.rice.krad.uif.util.ComponentBeanPostProcessor;
32  import org.kuali.rice.krad.uif.util.UifBeanFactoryPostProcessor;
33  import org.kuali.rice.krad.util.ObjectUtils;
34  import org.springframework.beans.PropertyValues;
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  import org.kuali.rice.krad.uif.UifConstants.ViewType;
43  
44  import java.beans.IntrospectionException;
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 DefaultListableBeanFactory ddBeans = new DefaultListableBeanFactory();
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     	BusinessObject boInstance;
497         try {
498             boInstance = (BusinessObject) boClass.newInstance();
499         } catch (Exception e) {
500         	throw new RuntimeException("Unable to instantiate BO: " + 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         String prefix = StringUtils.capitalize(propertyName);
706         String getName = "get" + prefix;
707         String isName = "is" + prefix;
708 
709         try {
710 
711             p = new PropertyDescriptor(propertyName, propertyClass, getName, null);
712 
713         }
714         catch (IntrospectionException e) {
715             try {
716 
717                 p = new PropertyDescriptor(propertyName, propertyClass, isName, null);
718 
719             }
720             catch (IntrospectionException f) {
721                 
722             }
723         }
724 
725         
726         if (null != p) {
727 
728             if (null == m) {
729 
730                 m = new TreeMap<String, PropertyDescriptor>();
731                 cache.put(propertyClassName, m);
732 
733             }
734 
735             m.put(propertyName, p);
736 
737         }
738 
739         return p;
740     }
741 
742     public Set<InactivationBlockingMetadata> getAllInactivationBlockingMetadatas(Class blockedClass) {
743     	return ddMapper.getAllInactivationBlockingMetadatas(ddIndex, blockedClass);
744     }
745     
746     
747 
748 
749     
750     public void performBeanOverrides()
751     {
752     	Collection<BeanOverride> beanOverrides = ddBeans.getBeansOfType(BeanOverride.class).values();
753     	
754     	if (beanOverrides.isEmpty()){
755     		LOG.info("DataDictionary.performOverrides(): No beans to override");
756     	}
757 		for (BeanOverride beanOverride : beanOverrides) {
758 			
759 			Object bean = ddBeans.getBean(beanOverride.getBeanName());
760 			beanOverride.performOverride(bean);
761 			LOG.info("DataDictionary.performOverrides(): Performing override on bean: " + bean.toString());
762 		}
763     }
764 
765 }