Coverage Report - org.mule.config.spring.parsers.assembly.DefaultBeanAssembler
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultBeanAssembler
0%
0/169
0%
0/94
2.96
 
 1  
 /*
 2  
  * $Id: DefaultBeanAssembler.java 19191 2010-08-25 21:05:23Z tcarlson $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 
 11  
 package org.mule.config.spring.parsers.assembly;
 12  
 
 13  
 import org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate;
 14  
 import org.mule.config.spring.parsers.assembly.configuration.PropertyConfiguration;
 15  
 import org.mule.config.spring.parsers.assembly.configuration.SingleProperty;
 16  
 import org.mule.config.spring.parsers.assembly.configuration.SinglePropertyLiteral;
 17  
 import org.mule.config.spring.parsers.assembly.configuration.SinglePropertyWrapper;
 18  
 import org.mule.config.spring.parsers.collection.ChildListEntryDefinitionParser;
 19  
 import org.mule.config.spring.parsers.collection.ChildMapEntryDefinitionParser;
 20  
 import org.mule.config.spring.util.SpringXMLUtils;
 21  
 import org.mule.util.ClassUtils;
 22  
 import org.mule.util.MapCombiner;
 23  
 
 24  
 import java.lang.reflect.Method;
 25  
 import java.util.Collection;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 import java.util.StringTokenizer;
 29  
 
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 import org.springframework.beans.MutablePropertyValues;
 33  
 import org.springframework.beans.PropertyValue;
 34  
 import org.springframework.beans.PropertyValues;
 35  
 import org.springframework.beans.factory.config.BeanDefinition;
 36  
 import org.springframework.beans.factory.config.MapFactoryBean;
 37  
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 38  
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 39  
 import org.springframework.beans.factory.support.ManagedList;
 40  
 import org.springframework.beans.factory.support.ManagedMap;
 41  
 import org.w3c.dom.Attr;
 42  
 
 43  
 public class DefaultBeanAssembler implements BeanAssembler
 44  
 {
 45  
 
 46  0
     private static Log logger = LogFactory.getLog(DefaultBeanAssembler.class);
 47  
     private PropertyConfiguration beanConfig;
 48  
     protected BeanDefinitionBuilder bean;
 49  
     protected PropertyConfiguration targetConfig;
 50  
     protected BeanDefinition target;
 51  
 
 52  
     public DefaultBeanAssembler(PropertyConfiguration beanConfig, BeanDefinitionBuilder bean,
 53  
                                 PropertyConfiguration targetConfig, BeanDefinition target)
 54  0
     {
 55  0
         this.beanConfig = beanConfig;
 56  0
         this.bean = bean;
 57  0
         this.targetConfig = targetConfig;
 58  0
         this.target = target;
 59  0
     }
 60  
 
 61  
     public BeanDefinitionBuilder getBean()
 62  
     {
 63  0
         return bean;
 64  
     }
 65  
 
 66  
     protected void setBean(BeanDefinitionBuilder bean)
 67  
     {
 68  0
         this.bean = bean;
 69  0
     }
 70  
 
 71  
     public BeanDefinition getTarget()
 72  
     {
 73  0
         return target;
 74  
     }
 75  
 
 76  
     protected PropertyConfiguration getBeanConfig()
 77  
     {
 78  0
         return beanConfig;
 79  
     }
 80  
 
 81  
     protected PropertyConfiguration getTargetConfig()
 82  
     {
 83  0
         return targetConfig;
 84  
     }
 85  
 
 86  
     /**
 87  
      * Add a property defined by an attribute to the bean we are constructing.
 88  
      *
 89  
      * <p>Since an attribute value is always a string, we don't have to deal with complex types
 90  
      * here - the only issue is whether or not we have a reference.  References are detected
 91  
      * by explicit annotation or by the "-ref" at the end of an attribute name.  We do not
 92  
      * check the Spring repo to see if a name already exists since that could lead to
 93  
      * unpredictable behaviour.
 94  
      * (see {@link org.mule.config.spring.parsers.assembly.configuration.PropertyConfiguration})
 95  
      * @param attribute The attribute to add
 96  
      */
 97  
     public void extendBean(Attr attribute)
 98  
     {
 99  0
         String oldName = SpringXMLUtils.attributeName(attribute);
 100  0
         if (!beanConfig.isIgnored(oldName))
 101  
         {
 102  0
             logger.debug(attribute + " for " + bean.getBeanDefinition().getBeanClassName());
 103  0
             String oldValue = attribute.getNodeValue();
 104  0
             String newName = bestGuessName(beanConfig, oldName, bean.getBeanDefinition().getBeanClassName());
 105  0
             Object newValue = beanConfig.translateValue(oldName, oldValue);
 106  0
             addPropertyWithReference(bean.getBeanDefinition().getPropertyValues(),
 107  
                     beanConfig.getSingleProperty(oldName), newName, newValue);
 108  
         }
 109  0
     }
 110  
 
 111  
     /**
 112  
      * Allow direct access to bean for major hacks
 113  
      *
 114  
      * @param newName The property name to add
 115  
      * @param newValue The property value to add
 116  
      * @param isReference If true, a bean reference is added (and newValue must be a String)
 117  
      */
 118  
     public void extendBean(String newName, Object newValue, boolean isReference)
 119  
     {
 120  0
         addPropertyWithReference(bean.getBeanDefinition().getPropertyValues(),
 121  
                 new SinglePropertyLiteral(isReference), newName, newValue);
 122  0
     }
 123  
 
 124  
     /**
 125  
      * Add a property defined by an attribute to the parent of the bean we are constructing.
 126  
      *
 127  
      * <p>This is unusual.  Normally you want {@link #extendBean(org.w3c.dom.Attr)}.
 128  
      * @param attribute The attribute to add
 129  
      */
 130  
     public void extendTarget(Attr attribute)
 131  
     {
 132  0
         String oldName = SpringXMLUtils.attributeName(attribute);
 133  0
         String oldValue = attribute.getNodeValue();
 134  0
         String newName = bestGuessName(targetConfig, oldName, bean.getBeanDefinition().getBeanClassName());
 135  0
         Object newValue = targetConfig.translateValue(oldName, oldValue);
 136  0
         addPropertyWithReference(target.getPropertyValues(),
 137  
                 targetConfig.getSingleProperty(oldName), newName, newValue);
 138  0
     }
 139  
 
 140  
     /**
 141  
      * Allow direct access to target for major hacks
 142  
      *
 143  
      * @param newName The property name to add
 144  
      * @param newValue The property value to add
 145  
      * @param isReference If true, a bean reference is added (and newValue must be a String)
 146  
      */
 147  
     public void extendTarget(String newName, Object newValue, boolean isReference)
 148  
     {
 149  0
         assertTargetPresent();
 150  0
         addPropertyWithReference(target.getPropertyValues(),
 151  
                 new SinglePropertyLiteral(isReference), newName, newValue);
 152  0
     }
 153  
     
 154  
     public void extendTarget(String oldName, String newName, Object newValue)
 155  
     {
 156  0
         assertTargetPresent();
 157  0
         addPropertyWithReference(target.getPropertyValues(),
 158  
                 new SinglePropertyWrapper(oldName, getTargetConfig()), newName, newValue);
 159  0
     }
 160  
 
 161  
     /**
 162  
      * Insert the bean we have built into the target (typically the parent bean).
 163  
      *
 164  
      * <p>This is the most complex case because the bean can have an aribtrary type.
 165  
      * 
 166  
      * @param oldName The identifying the bean (typically element name).
 167  
      */
 168  
     public void insertBeanInTarget(String oldName)
 169  
     {
 170  0
         logger.debug("insert " + bean.getBeanDefinition().getBeanClassName() + " -> " + target.getBeanClassName());
 171  0
         assertTargetPresent();
 172  0
         String beanClass = bean.getBeanDefinition().getBeanClassName();
 173  0
         PropertyValues sourceProperties = bean.getRawBeanDefinition().getPropertyValues();
 174  0
         String newName = bestGuessName(targetConfig, oldName, target.getBeanClassName());
 175  0
         MutablePropertyValues targetProperties = target.getPropertyValues();
 176  0
         PropertyValue pv = targetProperties.getPropertyValue(newName);
 177  0
         Object oldValue = null == pv ? null : pv.getValue();
 178  
 
 179  0
         if (! targetConfig.isIgnored(oldName))
 180  
         {
 181  0
             if (targetConfig.isCollection(oldName) ||
 182  
                     beanClass.equals(ChildListEntryDefinitionParser.ListEntry.class.getName()))
 183  
             {
 184  0
                 if (null == oldValue)
 185  
                 {
 186  0
                     if (beanClass.equals(ChildMapEntryDefinitionParser.KeyValuePair.class.getName()) ||
 187  
                             beanClass.equals(MapEntryCombiner.class.getName()) ||
 188  
                             beanClass.equals(MapFactoryBean.class.getName()))
 189  
                     {
 190  
                         // a collection of maps requires an extra intermediate object that does the
 191  
                         // lazy combination/caching of maps when first used
 192  0
                         BeanDefinitionBuilder combiner = BeanDefinitionBuilder.rootBeanDefinition(MapCombiner.class);
 193  0
                         targetProperties.addPropertyValue(newName, combiner.getBeanDefinition());
 194  0
                         MutablePropertyValues combinerProperties = combiner.getBeanDefinition().getPropertyValues();
 195  0
                         oldValue = new ManagedList();
 196  0
                         pv = new PropertyValue(MapCombiner.LIST, oldValue);
 197  0
                         combinerProperties.addPropertyValue(pv);
 198  0
                     }
 199  
                     else
 200  
                     {
 201  0
                         oldValue = new ManagedList();
 202  0
                         pv = new PropertyValue(newName, oldValue);
 203  0
                         targetProperties.addPropertyValue(pv);
 204  
                     }
 205  
                 }
 206  
 
 207  0
                 List list = retrieveList(oldValue);
 208  0
                 if (ChildMapEntryDefinitionParser.KeyValuePair.class.getName().equals(beanClass))
 209  
                 {
 210  0
                     list.add(new ManagedMap());
 211  0
                     retrieveMap(list.get(list.size() - 1)).put(
 212  
                             sourceProperties.getPropertyValue(ChildMapEntryDefinitionParser.KEY).getValue(),
 213  
                             sourceProperties.getPropertyValue(ChildMapEntryDefinitionParser.VALUE).getValue());
 214  
                 }
 215  0
                 else if (beanClass.equals(ChildListEntryDefinitionParser.ListEntry.class.getName()))
 216  
                 {
 217  0
                     list.add(sourceProperties.getPropertyValue(ChildListEntryDefinitionParser.VALUE).getValue());
 218  
                 }
 219  
                 else
 220  
                 {
 221  0
                     list.add(bean.getBeanDefinition());
 222  
                 }
 223  0
             }
 224  
             else
 225  
             {
 226  
                 // not a collection
 227  
 
 228  0
                 if (ChildMapEntryDefinitionParser.KeyValuePair.class.getName().equals(beanClass))
 229  
                 {
 230  0
                     if (null == pv || null == oldValue)
 231  
                     {
 232  0
                         pv = new PropertyValue(newName, new ManagedMap());
 233  0
                         targetProperties.addPropertyValue(pv);
 234  
                     }
 235  0
                     retrieveMap(pv.getValue()).put(
 236  
                             sourceProperties.getPropertyValue(ChildMapEntryDefinitionParser.KEY).getValue(),
 237  
                             sourceProperties.getPropertyValue(ChildMapEntryDefinitionParser.VALUE).getValue());
 238  
                 }
 239  
                 else
 240  
                 {
 241  0
                     targetProperties.addPropertyValue(newName, bean.getBeanDefinition());
 242  
                 }
 243  
             }
 244  
         }
 245  0
     }
 246  
     
 247  
     public void insertSingletonBeanInTarget(String propertyName, String singletonName)
 248  
     {
 249  0
         String newName = bestGuessName(targetConfig, propertyName, target.getBeanClassName());
 250  
 
 251  0
         MutablePropertyValues targetProperties = target.getPropertyValues();
 252  0
         PropertyValue pv = targetProperties.getPropertyValue(newName);
 253  0
         Object oldValue = null == pv ? null : pv.getValue();
 254  
 
 255  0
         if (!targetConfig.isIgnored(propertyName))
 256  
         {
 257  0
             if (targetConfig.isCollection(propertyName))
 258  
             {
 259  0
                 if (null == oldValue)
 260  
                 {
 261  0
                     oldValue = new ManagedList();
 262  0
                     pv = new PropertyValue(newName, oldValue);
 263  0
                     targetProperties.addPropertyValue(pv);
 264  
                 }
 265  
 
 266  0
                 List list = retrieveList(oldValue);
 267  0
                 list.add(new RuntimeBeanReference(singletonName));
 268  0
             }
 269  
             else
 270  
             {
 271  
                 // not a collection
 272  0
                 targetProperties.addPropertyValue(newName, new RuntimeBeanReference(singletonName));
 273  
             }
 274  
         }
 275  
         // getTarget().getPropertyValues().addPropertyValue(newName, new RuntimeBeanReference(singletonName));
 276  0
     }
 277  
     
 278  
     protected void insertInTarget(String oldName){
 279  
         
 280  0
     }
 281  
 
 282  
     protected static List retrieveList(Object value)
 283  
     {
 284  0
         if (value instanceof List)
 285  
         {
 286  0
             return (List) value;
 287  
         }
 288  0
         else if (isDefinitionOf(value, MapCombiner.class))
 289  
         {
 290  0
             return (List) unpackDefinition(value, MapCombiner.LIST);
 291  
         }
 292  
         else
 293  
         {
 294  0
             throw new ClassCastException("Collection not of expected type: " + value);
 295  
         }
 296  
     }
 297  
 
 298  
     private static Map retrieveMap(Object value)
 299  
     {
 300  0
         if (value instanceof Map)
 301  
         {
 302  0
             return (Map) value;
 303  
         }
 304  0
         else if (isDefinitionOf(value, MapFactoryBean.class))
 305  
         {
 306  0
             return (Map) unpackDefinition(value, "sourceMap");
 307  
         }
 308  
         else
 309  
         {
 310  0
             throw new ClassCastException("Map not of expected type: " + value);
 311  
         }
 312  
     }
 313  
 
 314  
     private static boolean isDefinitionOf(Object value, Class clazz)
 315  
     {
 316  0
         return value instanceof BeanDefinition &&
 317  
                 ((BeanDefinition) value).getBeanClassName().equals(clazz.getName());
 318  
     }
 319  
 
 320  
     private static Object unpackDefinition(Object definition, String name)
 321  
     {
 322  0
         return ((BeanDefinition) definition).getPropertyValues().getPropertyValue(name).getValue();
 323  
     }
 324  
 
 325  
 
 326  
     /**
 327  
      * Copy the properties from the bean we have been building into the target (typically
 328  
      * the parent bean).  In other words, the bean is a facade for the target.
 329  
      *
 330  
      * <p>This assumes that the source bean has been constructed correctly (ie the decisions about
 331  
      * what is ignored, a map, a list, etc) have already been made.   All it does (apart from a
 332  
      * direct copy) is merge collections with those on the target when necessary.
 333  
      */
 334  
     public void copyBeanToTarget()
 335  
     {
 336  0
         logger.debug("copy " + bean.getBeanDefinition().getBeanClassName() + " -> " + target.getBeanClassName());
 337  0
         assertTargetPresent();
 338  0
         MutablePropertyValues targetProperties = target.getPropertyValues();
 339  0
         MutablePropertyValues beanProperties = bean.getBeanDefinition().getPropertyValues();
 340  0
         for (int i=0;i < beanProperties.size(); i++)
 341  
         {
 342  0
             PropertyValue propertyValue = beanProperties.getPropertyValues()[i];
 343  0
             addPropertyWithoutReference(targetProperties, new SinglePropertyLiteral(),
 344  
                     propertyValue.getName(), propertyValue.getValue());
 345  
         }
 346  0
     }
 347  
 
 348  
     public void setBeanFlag(String flag)
 349  
     {
 350  0
         MuleHierarchicalBeanDefinitionParserDelegate.setFlag(bean.getRawBeanDefinition(), flag);
 351  0
     }
 352  
 
 353  
     protected void assertTargetPresent()
 354  
     {
 355  0
         if (null == target)
 356  
         {
 357  0
             throw new IllegalStateException("Bean assembler does not have a target");
 358  
         }
 359  0
     }
 360  
 
 361  
     protected void addPropertyWithReference(MutablePropertyValues properties, SingleProperty config,
 362  
                                             String name, Object value)
 363  
     {
 364  0
         if (!config.isIgnored())
 365  
         {
 366  0
             if (config.isReference())
 367  
             {
 368  0
                 if (value instanceof String)
 369  
                 {
 370  0
                     if (((String) value).trim().indexOf(" ") > -1)
 371  
                     {
 372  0
                         config.setCollection();
 373  
                     }
 374  0
                     for (StringTokenizer refs = new StringTokenizer((String) value); refs.hasMoreTokens();)
 375  
                     {
 376  0
                         String ref = refs.nextToken();
 377  0
                         if (logger.isDebugEnabled())
 378  
                         {
 379  0
                             logger.debug("possible non-dependent reference: " + name + "/" + ref);
 380  
                         }
 381  0
                         addPropertyWithoutReference(properties, config, name, new RuntimeBeanReference(ref));
 382  0
                     }
 383  
                 }
 384  
                 else
 385  
                 {
 386  0
                     throw new IllegalArgumentException("Bean reference must be a String: " + name + "/" + value);
 387  
                 }
 388  
             }
 389  
             else
 390  
             {
 391  0
                 addPropertyWithoutReference(properties, config, name, value);
 392  
             }
 393  
         }
 394  0
     }
 395  
 
 396  
     protected void addPropertyWithoutReference(MutablePropertyValues properties, SingleProperty config,
 397  
                                                String name, Object value)
 398  
     {
 399  0
         if (!config.isIgnored())
 400  
         {
 401  0
             if (logger.isDebugEnabled())
 402  
             {
 403  0
                 logger.debug(name + ": " + value);
 404  
             }
 405  0
             Object oldValue = null;
 406  0
             if (properties.contains(name))
 407  
             {
 408  0
                 oldValue = properties.getPropertyValue(name).getValue();
 409  
             }
 410  
             // merge collections
 411  0
             if (config.isCollection() || oldValue instanceof Collection || value instanceof Collection)
 412  
             {
 413  0
                 Collection values = new ManagedList();
 414  0
                 if (null != oldValue)
 415  
                 {
 416  0
                     properties.removePropertyValue(name);
 417  0
                     if (oldValue instanceof Collection)
 418  
                     {
 419  0
                         values.addAll((Collection) oldValue);
 420  
                     }
 421  
                     else
 422  
                     {
 423  0
                         values.add(oldValue);
 424  
                     }
 425  
                 }
 426  0
                 if (value instanceof Collection)
 427  
                 {
 428  0
                     values.addAll((Collection) value);
 429  
                 }
 430  
                 else
 431  
                 {
 432  0
                     values.add(value);
 433  
                 }
 434  0
                 properties.addPropertyValue(name, values);
 435  0
             }
 436  
             else
 437  
             {
 438  0
                 properties.addPropertyValue(name, value);
 439  
             }
 440  
         }
 441  0
     }
 442  
 
 443  
     protected static String bestGuessName(PropertyConfiguration config, String oldName, String className)
 444  
     {
 445  0
         String newName = config.translateName(oldName);
 446  0
         if (! methodExists(className, newName))
 447  
         {
 448  0
             String plural = newName + "s";
 449  0
             if (methodExists(className, plural))
 450  
             {
 451  
                 // this lets us avoid setting addCollection in the majority of cases
 452  0
                 config.addCollection(oldName);
 453  0
                 return plural;
 454  
             }
 455  0
             if (newName.endsWith("y"))
 456  
             {
 457  0
                 String pluraly = newName.substring(0, newName.length()-1) + "ies";
 458  0
                 if (methodExists(className, pluraly))
 459  
                 {
 460  
                     // this lets us avoid setting addCollection in the majority of cases
 461  0
                     config.addCollection(oldName);
 462  0
                     return pluraly;
 463  
                 }
 464  
             }
 465  
         }
 466  0
         return newName;
 467  
     }
 468  
 
 469  
     protected static boolean methodExists(String className, String newName)
 470  
     {
 471  
         try
 472  
         {
 473  
             // is there a better way than this?!
 474  
             // BeanWrapperImpl instantiates an instance, which we don't want.
 475  
             // if there really is no better way, i guess it should go in
 476  
             // class or bean utils.
 477  0
             Class clazz = ClassUtils.getClass(className);
 478  0
             Method[] methods = clazz.getMethods();
 479  0
             String setter = "set" + newName;
 480  0
             for (int i = 0; i < methods.length; ++i)
 481  
             {
 482  0
                 if (methods[i].getName().equalsIgnoreCase(setter))
 483  
                 {
 484  0
                     return true;
 485  
                 }
 486  
             }
 487  
         }
 488  0
         catch (Exception e)
 489  
         {
 490  0
             logger.debug("Could not access bean class " + className, e);
 491  0
         }
 492  0
         return false;
 493  
     }
 494  
 
 495  
 
 496  
 }