View Javadoc
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.util;
8   
9   import org.mule.config.i18n.CoreMessages;
10  
11  import java.lang.reflect.Field;
12  import java.lang.reflect.InvocationTargetException;
13  import java.lang.reflect.Method;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.Map;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  
21  /**
22   * <code>BeanUtils</code> provides functions for altering the way commons BeanUtils
23   * works
24   */
25  // @ThreadSafe
26  public class BeanUtils extends org.apache.commons.beanutils.BeanUtils
27  {
28      public static final String SET_PROPERTIES_METHOD = "setProperties";
29  
30      /**
31       * logger used by this class
32       */
33      private static final Log logger = LogFactory.getLog(BeanUtils.class);
34  
35      /**
36       * Exception safe version of BeanUtils.populate()
37       *
38       * @param object      the object to set the properties on
39       * @param props       the map of properties to set
40       * @param logWarnings whether exception warnings should be logged
41       */
42      public static void populateWithoutFail(Object object, Map props, boolean logWarnings)
43      {
44          // Check to see if our object has a setProperties method where the properties
45          // map should be set
46          if (ClassUtils.getMethod(object.getClass(), SET_PROPERTIES_METHOD, new Class[]{Map.class}) != null)
47          {
48              try
49              {
50                  BeanUtils.setProperty(object, "properties", props);
51              }
52              catch (Exception e)
53              {
54                  // this should never happen since we explicitly check for the method
55                  // above
56                  if (logWarnings)
57                  {
58                      logger.warn("Property: " + SET_PROPERTIES_METHOD + "=" + Map.class.getName()
59                              + " not found on object: " + object.getClass().getName());
60                  }
61              }
62          }
63          else
64          {
65              for (Iterator iterator = props.entrySet().iterator(); iterator.hasNext();)
66              {
67                  Map.Entry entry = (Map.Entry) iterator.next();
68  
69                  try
70                  {
71                      BeanUtils.setProperty(object, entry.getKey().toString(), entry.getValue());
72                  }
73                  catch (Exception e)
74                  {
75                      if (logWarnings)
76                      {
77                          logger.warn("Property: " + entry.getKey() + "=" + entry.getValue()
78                                  + " not found on object: " + object.getClass().getName());
79                      }
80                  }
81              }
82          }
83      }
84  
85      /**
86       * This will overlay a map of properties on a bean.  This method will validate that all properties are available
87       * on the bean before setting the properties
88       *
89       * @param bean  the bean on which to set the properties
90       * @param props a Map of properties to set on the bean
91       * @throws IllegalAccessException
92       * @throws InvocationTargetException
93       */
94      public static void populate(Object bean, Map props) throws IllegalAccessException, InvocationTargetException
95      {
96          // Check to see if our object has a setProperties method where the properties
97          // map should be set
98          if (ClassUtils.getMethod(bean.getClass(), SET_PROPERTIES_METHOD, new Class[]{Map.class}) != null)
99          {
100             BeanUtils.setProperty(bean, "properties", props);
101         }
102         else
103         {
104             Map master = describe(bean);
105             for (Iterator iterator = props.keySet().iterator(); iterator.hasNext();)
106             {
107                 Object o = iterator.next();
108                 if (!master.containsKey(o))
109                 {
110                     throw new IllegalArgumentException(CoreMessages.propertyDoesNotExistOnObject(o.toString(), bean).getMessage());
111                 }
112 
113             }
114             org.apache.commons.beanutils.BeanUtils.populate(bean, props);
115         }
116     }
117 
118     /**
119      * The Apache BeanUtils version of this converts all values to String, which is pretty useless, it also includes
120      * stuff not defined by the user
121      *
122      * @param object the object to Describe
123      * @return a map of the properties on the object
124      */
125     public static Map describe(Object object)
126     {
127         Map props = new HashMap(object.getClass().getDeclaredFields().length);
128         for (int i = 0; i < object.getClass().getDeclaredFields().length; i++)
129         {
130             Field field = object.getClass().getDeclaredFields()[i];
131             field.setAccessible(true);
132             try
133             {
134                 props.put(field.getName(), field.get(object));
135             }
136             catch (IllegalAccessException e)
137             {
138                 logger.debug("Unable to read field: " + field.getName() + " on object: " + object);
139             }
140         }
141         return props;
142     }
143 
144     /**
145      * Similar to {@link #describe(Object)} except that it will only populate bean properties where there is a valid
146      * getter and setter method. Basically this method will describe a bean and honour its encapsulation.
147      *
148      * @param object the object to describe
149      * @return a map of published properties
150      */
151     public static Map<String, Object> describeBean(Object object)
152     {
153         Map<String, Object> props = new HashMap<String, Object>();
154         for (int i = 0; i < object.getClass().getMethods().length; i++)
155         {
156             Method method = object.getClass().getMethods()[i];
157             if (method.getName().startsWith("get") || method.getName().startsWith("is"))
158             {
159                 String field = (method.getName().startsWith("is") ? method.getName().substring(2) : method.getName().substring(3));
160                 String setter = "set" + field;
161                 try
162                 {
163                     object.getClass().getMethod(setter, method.getReturnType());
164                 }
165                 catch (NoSuchMethodException e)
166                 {
167                     logger.debug("Ignoring bean property: " + e.getMessage());
168                     continue;
169                 }
170                 field = field.substring(0, 1).toLowerCase() + field.substring(1);
171                 try
172                 {
173                     props.put(field, method.invoke(object));
174                 }
175                 catch (Exception e)
176                 {
177                     logger.debug("unable to call bean method: " + method);
178                 }
179             }
180         }
181         return props;
182     }
183 }