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 }