001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.datadictionary;
017
018 import org.apache.commons.beanutils.PropertyUtils;
019 import org.apache.commons.lang.ArrayUtils;
020 import org.apache.commons.lang.StringUtils;
021 import org.apache.commons.logging.Log;
022 import org.apache.commons.logging.LogFactory;
023 import org.kuali.rice.core.api.util.ClassLoaderUtils;
024 import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension;
025 import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
026 import org.kuali.rice.krad.datadictionary.exception.CompletionException;
027 import org.kuali.rice.krad.datadictionary.parse.StringListConverter;
028 import org.kuali.rice.krad.datadictionary.parse.StringMapConverter;
029 import org.kuali.rice.krad.service.KRADServiceLocator;
030 import org.kuali.rice.krad.service.PersistenceStructureService;
031 import org.kuali.rice.krad.uif.UifConstants.ViewType;
032 import org.kuali.rice.krad.uif.util.ComponentBeanPostProcessor;
033 import org.kuali.rice.krad.uif.util.UifBeanFactoryPostProcessor;
034 import org.kuali.rice.krad.uif.view.View;
035 import org.kuali.rice.krad.util.ObjectUtils;
036 import org.springframework.beans.PropertyValues;
037 import org.springframework.beans.factory.config.BeanPostProcessor;
038 import org.springframework.beans.factory.support.KualiDefaultListableBeanFactory;
039 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
040 import org.springframework.context.expression.StandardBeanExpressionResolver;
041 import org.springframework.core.convert.support.GenericConversionService;
042 import org.springframework.core.io.DefaultResourceLoader;
043 import org.springframework.core.io.Resource;
044
045 import java.beans.PropertyDescriptor;
046 import java.io.File;
047 import java.io.IOException;
048 import java.util.ArrayList;
049 import java.util.Collection;
050 import java.util.HashMap;
051 import java.util.List;
052 import java.util.Map;
053 import java.util.Set;
054 import java.util.TreeMap;
055
056 /**
057 * Collection of named BusinessObjectEntry objects, each of which contains
058 * information relating to the display, validation, and general maintenance of a
059 * BusinessObject.
060 */
061 public class DataDictionary {
062
063 protected KualiDefaultListableBeanFactory ddBeans = new KualiDefaultListableBeanFactory();
064 protected XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ddBeans);
065
066 // logger
067 private static final Log LOG = LogFactory.getLog(DataDictionary.class);
068
069 /**
070 * The encapsulation of DataDictionary indices
071 */
072 protected DataDictionaryIndex ddIndex = new DataDictionaryIndex(ddBeans);
073
074 // View indices
075 protected UifDictionaryIndex uifIndex = new UifDictionaryIndex(ddBeans);
076
077 /**
078 * The DataDictionaryMapper
079 * The default mapper simply consults the initialized indices
080 * on workflow document type
081 */
082 protected DataDictionaryMapper ddMapper = new DataDictionaryIndexMapper();
083
084 protected List<String> configFileLocations = new ArrayList<String>();
085
086
087 public List<String> getConfigFileLocations() {
088 return this.configFileLocations;
089 }
090
091 public void setConfigFileLocations(List<String> configFileLocations) {
092 this.configFileLocations = configFileLocations;
093 }
094
095 public void addConfigFileLocation( String location ) throws IOException {
096 indexSource( location );
097 }
098
099 /**
100 * Sets the DataDictionaryMapper
101 * @param mapper the datadictionary mapper
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 // configure the bean factory, setup component decorator post processor
155 // and allow Spring EL
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 // expand configuration locations into files
171 LOG.info("Starting DD XML File Load");
172
173 String[] configFileLocationsArray = new String[configFileLocations.size()];
174 configFileLocationsArray = configFileLocations.toArray(configFileLocationsArray);
175 configFileLocations.clear(); // empty the list out so other items can be added
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 // indexing
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 * @param className
220 * @return BusinessObjectEntry for the named class, or null if none exists
221 */
222 @Deprecated
223 public BusinessObjectEntry getBusinessObjectEntry(String className ) {
224 return ddMapper.getBusinessObjectEntry(ddIndex, className);
225 }
226
227 /**
228 * @param className
229 * @return BusinessObjectEntry for the named class, or null if none exists
230 */
231 public DataObjectEntry getDataObjectEntry(String className ) {
232 return ddMapper.getDataObjectEntry(ddIndex, className);
233 }
234
235 /**
236 * This method gets the business object entry for a concrete class
237 *
238 * @param className
239 * @return
240 */
241 public BusinessObjectEntry getBusinessObjectEntryForConcreteClass(String className){
242 return ddMapper.getBusinessObjectEntryForConcreteClass(ddIndex, className);
243 }
244
245 /**
246 * @return List of businessObject classnames
247 */
248 public List<String> getBusinessObjectClassNames() {
249 return ddMapper.getBusinessObjectClassNames(ddIndex);
250 }
251
252 /**
253 * @return Map of (classname, BusinessObjectEntry) pairs
254 */
255 public Map<String, BusinessObjectEntry> getBusinessObjectEntries() {
256 return ddMapper.getBusinessObjectEntries(ddIndex);
257 }
258
259 /**
260 * @param className
261 * @return DataDictionaryEntryBase for the named class, or null if none
262 * exists
263 */
264 public DataDictionaryEntry getDictionaryObjectEntry(String className) {
265 return ddMapper.getDictionaryObjectEntry(ddIndex, className);
266 }
267
268 /**
269 * Returns the KNS document entry for the given lookup key. The documentTypeDDKey is interpreted
270 * successively in the following ways until a mapping is found (or none if found):
271 * <ol>
272 * <li>KEW/workflow document type</li>
273 * <li>business object class name</li>
274 * <li>maintainable class name</li>
275 * </ol>
276 * This mapping is compiled when DataDictionary files are parsed on startup (or demand). Currently this
277 * means the mapping is static, and one-to-one (one KNS document maps directly to one and only
278 * one key).
279 *
280 * @param documentTypeDDKey the KEW/workflow document type name
281 * @return the KNS DocumentEntry if it exists
282 */
283 public DocumentEntry getDocumentEntry(String documentTypeDDKey ) {
284 return ddMapper.getDocumentEntry(ddIndex, documentTypeDDKey);
285 }
286
287 /**
288 * Note: only MaintenanceDocuments are indexed by businessObject Class
289 *
290 * This is a special case that is referenced in one location. Do we need
291 * another map for this stuff??
292 *
293 * @param businessObjectClass
294 * @return DocumentEntry associated with the given Class, or null if there
295 * is none
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 * Returns the View entry identified by the given id
307 *
308 * @param viewId - unique id for view
309 * @return View instance associated with the id
310 */
311 public View getViewById(String viewId) {
312 return ddMapper.getViewById(uifIndex, viewId);
313 }
314
315 /**
316 * Returns View instance identified by the view type name and index
317 *
318 * @param viewTypeName
319 * - type name for the view
320 * @param indexKey
321 * - Map of index key parameters, these are the parameters the
322 * indexer used to index the view initially and needs to identify
323 * an unique view instance
324 * @return View instance that matches the given index
325 */
326 public View getViewByTypeIndex(ViewType viewTypeName, Map<String, String> indexKey) {
327 return ddMapper.getViewByTypeIndex(uifIndex, viewTypeName, indexKey);
328 }
329
330 /**
331 * Indicates whether a <code>View</code> exists for the given view type and index information
332 *
333 * @param viewTypeName - type name for the view
334 * @param indexKey - Map of index key parameters, these are the parameters the
335 * indexer used to index the view initially and needs to identify
336 * an unique view instance
337 * @return boolean true if view exists, false if not
338 */
339 public boolean viewByTypeExist(ViewType viewTypeName, Map<String, String> indexKey) {
340 return ddMapper.viewByTypeExist(uifIndex, viewTypeName, indexKey);
341 }
342
343 /**
344 * Gets all <code>View</code> prototypes configured for the given view type
345 * name
346 *
347 * @param viewTypeName
348 * - view type name to retrieve
349 * @return List<View> view prototypes with the given type name, or empty
350 * list
351 */
352 public List<View> getViewsForType(ViewType viewTypeName) {
353 return ddMapper.getViewsForType(uifIndex, viewTypeName);
354 }
355
356 /**
357 * Returns an object from the dictionary by its spring bean name
358 *
359 * @param beanName - id or name for the bean definition
360 * @return Object object instance created or the singleton being maintained
361 */
362 public Object getDictionaryObject(String beanName) {
363 return ddBeans.getBean(beanName);
364 }
365
366 /**
367 * Indicates whether the data dictionary contains a bean with the given id
368 *
369 * @param id - id of the bean to check for
370 * @return boolean true if dictionary contains bean, false otherwise
371 */
372 public boolean containsDictionaryObject(String id) {
373 return ddBeans.containsBean(id);
374 }
375
376 /**
377 * Retrieves the configured property values for the view bean definition associated with the given id
378 *
379 * <p>
380 * Since constructing the View object can be expensive, when metadata only is needed this method can be used
381 * to retrieve the configured property values. Note this looks at the merged bean definition
382 * </p>
383 *
384 * @param viewId - id for the view to retrieve
385 * @return PropertyValues configured on the view bean definition, or null if view is not found
386 */
387 public PropertyValues getViewPropertiesById(String viewId) {
388 return ddMapper.getViewPropertiesById(uifIndex, viewId);
389 }
390
391 /**
392 * Retrieves the configured property values for the view bean definition associated with the given type and
393 * index
394 *
395 * <p>
396 * Since constructing the View object can be expensive, when metadata only is needed this method can be used
397 * to retrieve the configured property values. Note this looks at the merged bean definition
398 * </p>
399 *
400 * @param viewTypeName - type name for the view
401 * @param indexKey - Map of index key parameters, these are the parameters the indexer used to index
402 * the view initially and needs to identify an unique view instance
403 * @return PropertyValues configured on the view bean definition, or null if view is not found
404 */
405 public PropertyValues getViewPropertiesByType(ViewType viewTypeName, Map<String, String> indexKey) {
406 return ddMapper.getViewPropertiesByType(uifIndex, viewTypeName, indexKey);
407 }
408
409 /**
410 * @param targetClass
411 * @param propertyName
412 * @return true if the given propertyName names a property of the given class
413 * @throws CompletionException if there is a problem accessing the named property on the given class
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 * @param targetClass
431 * @param propertyName
432 * @return true if the given propertyName names a Collection property of the given class
433 * @throws CompletionException if there is a problem accessing the named property on the given class
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 * @return the persistenceStructureService
454 */
455 public static PersistenceStructureService getPersistenceStructureService() {
456 if ( persistenceStructureService == null ) {
457 persistenceStructureService = KRADServiceLocator.getPersistenceStructureService();
458 }
459 return persistenceStructureService;
460 }
461
462 /**
463 * This method determines the Class of the attributeName passed in. Null will be returned if the member is not available, or if
464 * a reflection exception is thrown.
465 *
466 * @param boClass - Class that the attributeName property exists in.
467 * @param attributeName - Name of the attribute you want a class for.
468 * @return The Class of the attributeName, if the attribute exists on the rootClass. Null otherwise.
469 */
470 public static Class getAttributeClass(Class boClass, String attributeName) {
471
472 // fail loudly if the attributeName isnt a member of rootClass
473 if (!isPropertyOf(boClass, attributeName)) {
474 throw new AttributeValidationException("unable to find attribute '" + attributeName + "' in rootClass '" + boClass.getName() + "'");
475 }
476
477 //Implementing Externalizable Business Object Services...
478 //The boClass can be an interface, hence handling this separately,
479 //since the original method was throwing exception if the class could not be instantiated.
480 if(boClass.isInterface())
481 return getAttributeClassWhenBOIsInterface(boClass, attributeName);
482 else
483 return getAttributeClassWhenBOIsClass(boClass, attributeName);
484
485 }
486
487 /**
488 *
489 * This method gets the property type of the given attributeName when the bo class is a concrete class
490 *
491 * @param boClass
492 * @param attributeName
493 * @return
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 // attempt to retrieve the class of the property
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 * This method gets the property type of the given attributeName when the bo class is an interface
514 * This method will also work if the bo class is not an interface,
515 * but that case requires special handling, hence a separate method getAttributeClassWhenBOIsClass
516 *
517 * @param boClass
518 * @param attributeName
519 * @return
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 // TODO: determine property type using generics type definition
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 * This method determines the Class of the elements in the collectionName passed in.
563 *
564 * @param boClass Class that the collectionName collection exists in.
565 * @param collectionName the name of the collection you want the element class for
566 * @return
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 * @param propertyClass
619 * @param propertyName
620 * @return PropertyDescriptor for the getter for the named property of the given class, if one exists.
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 * @param propertyClass
682 * @param propertyName
683 * @return PropertyDescriptor for the getter for the named property of the given class, if one exists.
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 // check to see if we've cached this descriptor already. if yes, return true.
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 // Use PropertyUtils.getPropertyDescriptors instead of manually constructing PropertyDescriptor because of
706 // issues with introspection and generic/co-variant return types
707 // See https://issues.apache.org/jira/browse/BEANUTILS-340 for more details
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 // cache the property descriptor if we found it.
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 * This method gathers beans of type BeanOverride and invokes each one's performOverride() method.
736 */
737 // KULRICE-4513
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 }