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