View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  // Contributors:  Georg Lundesgaard
15  package ch.qos.logback.core.joran.util;
16  
17  import ch.qos.logback.core.Context;
18  import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
19  import ch.qos.logback.core.joran.util.beans.BeanDescription;
20  import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
21  import ch.qos.logback.core.spi.ContextAwareBase;
22  import ch.qos.logback.core.util.AggregationType;
23  import ch.qos.logback.core.util.PropertySetterException;
24  import ch.qos.logback.core.util.StringUtil;
25  
26  import java.lang.reflect.Method;
27  
28  /**
29   * General purpose Object property setter. Clients repeatedly invokes
30   * {@link #setProperty setProperty(name,value)} in order to invoke setters on
31   * the Object specified in the constructor. This class relies on reflection to
32   * analyze the given Object Class.
33   *
34   * <p>
35   * Usage:
36   *
37   * <pre>
38   * PropertySetter ps = new PropertySetter(anObject);
39   * ps.set(&quot;name&quot;, &quot;Joe&quot;);
40   * ps.set(&quot;age&quot;, &quot;32&quot;);
41   * ps.set(&quot;isMale&quot;, &quot;true&quot;);
42   * </pre>
43   *
44   * will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and
45   * setMale(true) if such methods exist with those signatures. Otherwise an
46   * {@link PropertySetterException} is thrown.
47   *
48   * @author Anders Kristensen
49   * @author Ceki Gulcu
50   */
51  public class PropertySetter extends ContextAwareBase {
52  
53      protected final Object obj;
54      protected final Class<?> objClass;
55      protected final BeanDescription beanDescription;
56      protected final AggregationAssessor aggregationAssessor;
57  
58      /**
59       * Create a new PropertySetter for the specified Object. This is done in
60       * preparation for invoking {@link #setProperty} one or more times.
61       *
62       * @param obj the object for which to set properties
63       */
64      public PropertySetter(BeanDescriptionCache beanDescriptionCache, Object obj) {
65          this.obj = obj;
66          this.objClass = obj.getClass();
67          this.beanDescription = beanDescriptionCache.getBeanDescription(objClass);
68          this.aggregationAssessor = new AggregationAssessor(beanDescriptionCache, this.objClass);
69      }
70  
71      @Override
72      public void setContext(Context context) {
73          super.setContext(context);
74          aggregationAssessor.setContext(context);
75      }
76  
77  
78      /**
79       * Set a property on this PropertySetter's Object. If successful, this method
80       * will invoke a setter method on the underlying Object. The setter is the one
81       * for the specified property name and the value is determined partly from the
82       * setter argument type and partly from the value specified in the call to this
83       * method.
84       *
85       * <p>
86       * If the setter expects a String no conversion is necessary. If it expects an
87       * int, then an attempt is made to convert 'value' to an int using new
88       * Integer(value). If the setter expects a boolean, the conversion is by new
89       * Boolean(value).
90       *
91       * @param name  name of the property
92       * @param value String value of the property
93       */
94      public void setProperty(String name, String value) {
95          if (value == null) {
96              return;
97          }
98          Method setter = aggregationAssessor.findSetterMethod(name);
99          if (setter == null) {
100             addWarn("No setter for property [" + name + "] in " + objClass.getName() + ".");
101         } else {
102             try {
103                 setProperty(setter, value);
104             } catch (PropertySetterException ex) {
105                 addWarn("Failed to set property [" + name + "] to value \"" + value + "\". ", ex);
106             }
107         }
108     }
109 
110     /**
111      * Set the named property using a {@link Method setter}.
112      *
113      * @param setter  A Method describing the characteristics of the
114      *              property to set.
115      * @param value The value of the property.
116      */
117     private void setProperty(Method setter, String value) throws PropertySetterException {
118         Class<?>[] paramTypes = setter.getParameterTypes();
119 
120         Object arg;
121 
122         try {
123             arg = StringToObjectConverter.convertArg(this, value, paramTypes[0]);
124         } catch (Throwable t) {
125             throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed. ", t);
126         }
127 
128         if (arg == null) {
129             throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed.");
130         }
131         try {
132             setter.invoke(obj, arg);
133         } catch (Exception ex) {
134             throw new PropertySetterException(ex);
135         }
136     }
137 
138     public AggregationType computeAggregationType(String name) {
139         return this.aggregationAssessor.computeAggregationType(name);
140     }
141 
142 //    private Method findAdderMethod(String name) {
143 //        String propertyName = BeanUtil.toLowerCamelCase(name);
144 //        return beanDescription.getAdder(propertyName);
145 //    }
146 //
147 //    private Method findSetterMethod(String name) {
148 //        String propertyName = BeanUtil.toLowerCamelCase(name);
149 //        return beanDescription.getSetter(propertyName);
150 //    }
151 
152 //    private Class<?> getParameterClassForMethod(Method method) {
153 //        if (method == null) {
154 //            return null;
155 //        }
156 //        Class<?>[] classArray = method.getParameterTypes();
157 //        if (classArray.length != 1) {
158 //            return null;
159 //        } else {
160 //            return classArray[0];
161 //        }
162 //    }
163 
164 //    private AggregationType computeRawAggregationType(Method method) {
165 //        Class<?> parameterClass = getParameterClassForMethod(method);
166 //        if (parameterClass == null) {
167 //            return AggregationType.NOT_FOUND;
168 //        }
169 //        if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) {
170 //            return AggregationType.AS_BASIC_PROPERTY;
171 //        } else {
172 //            return AggregationType.AS_COMPLEX_PROPERTY;
173 //        }
174 //    }
175 
176 
177 
178     public Class<?> getObjClass() {
179         return objClass;
180     }
181 
182     public void addComplexProperty(String name, Object complexProperty) {
183         Method adderMethod = aggregationAssessor.findAdderMethod(name);
184         // first let us use the addXXX method
185         if (adderMethod != null) {
186             Class<?>[] paramTypes = adderMethod.getParameterTypes();
187             if (!isSanityCheckSuccessful(name, adderMethod, paramTypes, complexProperty)) {
188                 return;
189             }
190             invokeMethodWithSingleParameterOnThisObject(adderMethod, complexProperty);
191         } else {
192             addError("Could not find method [" + "add" + name + "] in class [" + objClass.getName() + "].");
193         }
194     }
195 
196     void invokeMethodWithSingleParameterOnThisObject(Method method, Object parameter) {
197         Class<?> ccc = parameter.getClass();
198         try {
199             method.invoke(this.obj, parameter);
200         } catch (Exception e) {
201             addError("Could not invoke method " + method.getName() + " in class " + obj.getClass().getName()
202                     + " with parameter of type " + ccc.getName(), e);
203         }
204     }
205 
206     public void addBasicProperty(String name, String strValue) {
207 
208         if (strValue == null) {
209             return;
210         }
211 
212         name = StringUtil.capitalizeFirstLetter(name);
213         Method adderMethod =aggregationAssessor.findAdderMethod(name);
214 
215         if (adderMethod == null) {
216             addError("No adder for property [" + name + "].");
217             return;
218         }
219 
220         Class<?>[] paramTypes = adderMethod.getParameterTypes();
221         isSanityCheckSuccessful(name, adderMethod, paramTypes, strValue);
222 
223         Object arg;
224         try {
225             arg = StringToObjectConverter.convertArg(this, strValue, paramTypes[0]);
226         } catch (Throwable t) {
227             addError("Conversion to type [" + paramTypes[0] + "] failed. ", t);
228             return;
229         }
230         if (arg != null) {
231             invokeMethodWithSingleParameterOnThisObject(adderMethod, strValue);
232         }
233     }
234 
235     public void setComplexProperty(String name, Object complexProperty) {
236         Method setter = aggregationAssessor.findSetterMethod(name);
237 
238         if (setter == null) {
239             addWarn("Not setter method for property [" + name + "] in " + obj.getClass().getName());
240 
241             return;
242         }
243 
244         Class<?>[] paramTypes = setter.getParameterTypes();
245 
246         if (!isSanityCheckSuccessful(name, setter, paramTypes, complexProperty)) {
247             return;
248         }
249         try {
250             invokeMethodWithSingleParameterOnThisObject(setter, complexProperty);
251 
252         } catch (Exception e) {
253             addError("Could not set component " + obj + " for parent component " + obj, e);
254         }
255     }
256 
257     private boolean isSanityCheckSuccessful(String name, Method method, Class<?>[] params, Object complexProperty) {
258         Class<?> ccc = complexProperty.getClass();
259         if (params.length != 1) {
260             addError("Wrong number of parameters in setter method for property [" + name + "] in "
261                     + obj.getClass().getName());
262 
263             return false;
264         }
265 
266         if (!params[0].isAssignableFrom(complexProperty.getClass())) {
267             addError("A \"" + ccc.getName() + "\" object is not assignable to a \"" + params[0].getName()
268                     + "\" variable.");
269             addError("The class \"" + params[0].getName() + "\" was loaded by ");
270             addError("[" + params[0].getClassLoader() + "] whereas object of type ");
271             addError("\"" + ccc.getName() + "\" was loaded by [" + ccc.getClassLoader() + "].");
272             return false;
273         }
274 
275         return true;
276     }
277 
278     public Object getObj() {
279         return obj;
280     }
281 
282 
283     public Class<?> getClassNameViaImplicitRules(String name, AggregationType aggregationType,
284             DefaultNestedComponentRegistry registry) {
285         return aggregationAssessor.getClassNameViaImplicitRules(name, aggregationType, registry);
286     }
287 
288 
289 
290 
291 
292     }