1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package ch.qos.logback.core.joran.spi;
19
20 import java.beans.BeanInfo;
21 import java.beans.IntrospectionException;
22 import java.beans.Introspector;
23 import java.beans.MethodDescriptor;
24 import java.beans.PropertyDescriptor;
25 import java.lang.annotation.Annotation;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.Modifier;
28
29 import ch.qos.logback.core.CoreConstants;
30 import ch.qos.logback.core.joran.action.IADataForComplexProperty;
31 import ch.qos.logback.core.spi.ContextAwareBase;
32 import ch.qos.logback.core.util.AggregationType;
33 import ch.qos.logback.core.util.PropertySetterException;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 public class PropertySetter extends ContextAwareBase {
58 private static final Class[] STING_CLASS_PARAMETER = new Class[] { String.class };
59
60 protected Object obj;
61 protected Class objClass;
62 protected PropertyDescriptor[] propertyDescriptors;
63 protected MethodDescriptor[] methodDescriptors;
64
65
66
67
68
69
70
71
72 public PropertySetter(Object obj) {
73 this.obj = obj;
74 this.objClass = obj.getClass();
75 }
76
77
78
79
80
81 protected void introspect() {
82 try {
83 BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
84 propertyDescriptors = bi.getPropertyDescriptors();
85 methodDescriptors = bi.getMethodDescriptors();
86 } catch (IntrospectionException ex) {
87 addError("Failed to introspect " + obj + ": " + ex.getMessage());
88 propertyDescriptors = new PropertyDescriptor[0];
89 methodDescriptors = new MethodDescriptor[0];
90 }
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public void setProperty(String name, String value) {
111 if (value == null) {
112 return;
113 }
114
115 name = Introspector.decapitalize(name);
116
117 PropertyDescriptor prop = getPropertyDescriptor(name);
118
119 if (prop == null) {
120 addWarn("No such property [" + name + "] in " + objClass.getName() + ".");
121 } else {
122 try {
123 setProperty(prop, name, value);
124 } catch (PropertySetterException ex) {
125 addWarn("Failed to set property [" + name + "] to value \"" + value
126 + "\". ", ex);
127 }
128 }
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 public void setProperty(PropertyDescriptor prop, String name, String value)
143 throws PropertySetterException {
144 Method setter = prop.getWriteMethod();
145
146 if (setter == null) {
147 throw new PropertySetterException("No setter for property [" + name
148 + "].");
149 }
150
151 Class[] paramTypes = setter.getParameterTypes();
152
153 if (paramTypes.length != 1) {
154 throw new PropertySetterException("#params for setter != 1");
155 }
156
157 Object arg;
158
159 try {
160 arg = convertArg(value, paramTypes[0]);
161 } catch (Throwable t) {
162 throw new PropertySetterException("Conversion to type [" + paramTypes[0]
163 + "] failed. ", t);
164 }
165
166 if (arg == null) {
167 throw new PropertySetterException("Conversion to type [" + paramTypes[0]
168 + "] failed.");
169 }
170 try {
171 setter.invoke(obj, arg);
172 } catch (Exception ex) {
173 throw new PropertySetterException(ex);
174 }
175 }
176
177 public AggregationType computeAggregationType(String name) {
178 String cName = capitalizeFirstLetter(name);
179
180 Method addMethod = findAdderMethod(cName);
181
182
183 if (addMethod != null) {
184 AggregationType type = computeRawAggregationType(addMethod);
185 switch (type) {
186 case NOT_FOUND:
187 return AggregationType.NOT_FOUND;
188 case AS_BASIC_PROPERTY:
189 return AggregationType.AS_BASIC_PROPERTY_COLLECTION;
190 case AS_COMPLEX_PROPERTY:
191 return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION;
192 }
193 }
194
195 Method setterMethod = findSetterMethod(name);
196 if (setterMethod != null) {
197 return computeRawAggregationType(setterMethod);
198 } else {
199
200 return AggregationType.NOT_FOUND;
201 }
202 }
203
204 private Method findAdderMethod(String name) {
205 name = capitalizeFirstLetter(name);
206 Method adderMethod = getMethod("add" + name);
207 return adderMethod;
208 }
209
210 private Method findSetterMethod(String name) {
211 String dName = Introspector.decapitalize(name);
212 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);
213 if (propertyDescriptor != null) {
214 return propertyDescriptor.getWriteMethod();
215 } else {
216 return null;
217 }
218 }
219
220 private Class<?> getParameterClassForMethod(Method method) {
221 if (method == null) {
222 return null;
223 }
224 Class[] classArray = method.getParameterTypes();
225 if (classArray.length != 1) {
226 return null;
227 } else {
228 return classArray[0];
229 }
230 }
231
232 private AggregationType computeRawAggregationType(Method method) {
233 Class<?> parameterClass = getParameterClassForMethod(method);
234 if (parameterClass == null) {
235 return AggregationType.NOT_FOUND;
236 } else {
237 Package p = parameterClass.getPackage();
238 if (parameterClass.isPrimitive()) {
239 return AggregationType.AS_BASIC_PROPERTY;
240 } else if (p != null && "java.lang".equals(p.getName())) {
241 return AggregationType.AS_BASIC_PROPERTY;
242 } else if (isBuildableFromString(parameterClass)) {
243 return AggregationType.AS_BASIC_PROPERTY;
244 } else if (parameterClass.isEnum()) {
245 return AggregationType.AS_BASIC_PROPERTY;
246 } else {
247 return AggregationType.AS_COMPLEX_PROPERTY;
248 }
249 }
250 }
251
252 public Class findUnequivocallyInstantiableClass(
253 IADataForComplexProperty actionData) {
254 Class<?> clazz;
255 AggregationType at = actionData.getAggregationType();
256 switch (at) {
257 case AS_COMPLEX_PROPERTY:
258 Method setterMethod = findSetterMethod(actionData
259 .getComplexPropertyName());
260 clazz = getParameterClassForMethod(setterMethod);
261 if (clazz != null && isUnequivocallyInstantiable(clazz)) {
262 return clazz;
263 } else {
264 return null;
265 }
266 case AS_COMPLEX_PROPERTY_COLLECTION:
267 Method adderMethod = findAdderMethod(actionData.getComplexPropertyName());
268 clazz = getParameterClassForMethod(adderMethod);
269 if (clazz != null && isUnequivocallyInstantiable(clazz)) {
270 return clazz;
271 } else {
272 return null;
273 }
274 default:
275 throw new IllegalArgumentException(at
276 + " is not valid type in this method");
277 }
278 }
279
280
281
282
283
284
285
286 private boolean isUnequivocallyInstantiable(Class<?> clazz) {
287 if (clazz.isInterface()) {
288 return false;
289 }
290
291
292
293 Object o;
294 try {
295 o = clazz.newInstance();
296 if (o != null) {
297 return true;
298 } else {
299 return false;
300 }
301 } catch (InstantiationException e) {
302 return false;
303 } catch (IllegalAccessException e) {
304 return false;
305 }
306 }
307
308 public Class getObjClass() {
309 return objClass;
310 }
311
312 public void addComplexProperty(String name, Object complexProperty) {
313 Method adderMethod = findAdderMethod(name);
314
315 if (adderMethod != null) {
316 Class[] paramTypes = adderMethod.getParameterTypes();
317 if (!isSanityCheckSuccessful(name, adderMethod, paramTypes,
318 complexProperty)) {
319 return;
320 }
321 invokeMethodWithSingleParameterOnThisObject(adderMethod, complexProperty);
322 } else {
323 addError("Could not find method [" + "add" + name + "] in class ["
324 + objClass.getName() + "].");
325 }
326 }
327
328 void invokeMethodWithSingleParameterOnThisObject(Method method,
329 Object parameter) {
330 Class ccc = parameter.getClass();
331 try {
332 method.invoke(this.obj, parameter);
333 } catch (Exception e) {
334 addError("Could not invoke method " + method.getName() + " in class "
335 + obj.getClass().getName() + " with parameter of type "
336 + ccc.getName(), e);
337 }
338 }
339
340 @SuppressWarnings("unchecked")
341 public void addBasicProperty(String name, String strValue) {
342
343 if (strValue == null) {
344 return;
345 }
346
347 name = capitalizeFirstLetter(name);
348 Method adderMethod = findAdderMethod(name);
349
350 if (adderMethod == null) {
351 addError("No adder for property [" + name + "].");
352 return;
353 }
354
355 Class[] paramTypes = adderMethod.getParameterTypes();
356 isSanityCheckSuccessful(name, adderMethod, paramTypes, strValue);
357
358 Object arg;
359 try {
360 arg = convertArg(strValue, paramTypes[0]);
361 } catch (Throwable t) {
362 addError("Conversion to type [" + paramTypes[0] + "] failed. ", t);
363 return;
364 }
365 if (arg != null) {
366 invokeMethodWithSingleParameterOnThisObject(adderMethod, strValue);
367 }
368 }
369
370 public void setComplexProperty(String name, Object complexProperty) {
371 String dName = Introspector.decapitalize(name);
372 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);
373
374 if (propertyDescriptor == null) {
375 addWarn("Could not find PropertyDescriptor for [" + name + "] in "
376 + objClass.getName());
377
378 return;
379 }
380
381 Method setter = propertyDescriptor.getWriteMethod();
382
383 if (setter == null) {
384 addWarn("Not setter method for property [" + name + "] in "
385 + obj.getClass().getName());
386
387 return;
388 }
389
390 Class[] paramTypes = setter.getParameterTypes();
391
392 if (!isSanityCheckSuccessful(name, setter, paramTypes, complexProperty)) {
393 return;
394 }
395 try {
396 invokeMethodWithSingleParameterOnThisObject(setter, complexProperty);
397
398 } catch (Exception e) {
399 addError("Could not set component " + obj + " for parent component "
400 + obj, e);
401 }
402 }
403
404 private boolean isSanityCheckSuccessful(String name, Method method,
405 Class<?>[] params, Object complexProperty) {
406 Class ccc = complexProperty.getClass();
407 if (params.length != 1) {
408 addError("Wrong number of parameters in setter method for property ["
409 + name + "] in " + obj.getClass().getName());
410
411 return false;
412 }
413
414 if (!params[0].isAssignableFrom(complexProperty.getClass())) {
415 addError("A \"" + ccc.getName() + "\" object is not assignable to a \""
416 + params[0].getName() + "\" variable.");
417 addError("The class \"" + params[0].getName() + "\" was loaded by ");
418 addError("[" + params[0].getClassLoader() + "] whereas object of type ");
419 addError("\"" + ccc.getName() + "\" was loaded by ["
420 + ccc.getClassLoader() + "].");
421 return false;
422 }
423
424 return true;
425 }
426
427 private String capitalizeFirstLetter(String name) {
428 return name.substring(0, 1).toUpperCase() + name.substring(1);
429 }
430
431
432
433
434 protected Object convertArg(String val, Class<?> type) {
435 if (val == null) {
436 return null;
437 }
438 String v = val.trim();
439 if (String.class.isAssignableFrom(type)) {
440 return val;
441 } else if (Integer.TYPE.isAssignableFrom(type)) {
442 return new Integer(v);
443 } else if (Long.TYPE.isAssignableFrom(type)) {
444 return new Long(v);
445 } else if (Float.TYPE.isAssignableFrom(type)) {
446 return new Float(v);
447 } else if (Double.TYPE.isAssignableFrom(type)) {
448 return new Double(v);
449 } else if (Boolean.TYPE.isAssignableFrom(type)) {
450 if ("true".equalsIgnoreCase(v)) {
451 return Boolean.TRUE;
452 } else if ("false".equalsIgnoreCase(v)) {
453 return Boolean.FALSE;
454 }
455 } else if (type.isEnum()) {
456 return convertEnum(val, type);
457 } else if (isBuildableFromString(type)) {
458 return buildFromString(type, val);
459 }
460
461 return null;
462 }
463
464 boolean isBuildableFromString(Class<?> parameterClass) {
465 try {
466 Method valueOfMethod = parameterClass.getMethod(CoreConstants.VALUE_OF,
467 STING_CLASS_PARAMETER);
468 int mod = valueOfMethod.getModifiers();
469 if (Modifier.isStatic(mod)) {
470 return true;
471 }
472 } catch (SecurityException e) {
473
474 } catch (NoSuchMethodException e) {
475
476 }
477 return false;
478 }
479
480 Object buildFromString(Class<?> type, String val) {
481 try {
482 Method valueOfMethod = type.getMethod(CoreConstants.VALUE_OF,
483 STING_CLASS_PARAMETER);
484 return valueOfMethod.invoke(null, val);
485 } catch (Exception e) {
486 addError("Failed to invoke " + CoreConstants.VALUE_OF
487 + "{} method in class [" + type.getName() + "] with value [" + val
488 + "]");
489 return null;
490 }
491 }
492
493 protected Object convertEnum(String val, Class<?> type) {
494 try {
495 Method m = type.getMethod(CoreConstants.VALUE_OF, STING_CLASS_PARAMETER);
496 return m.invoke(null, val);
497 } catch (Exception e) {
498 addError("Failed to convert value [" + val + "] to enum ["
499 + type.getName() + "]", e);
500 }
501 return null;
502 }
503
504 protected Method getMethod(String methodName) {
505 if (methodDescriptors == null) {
506 introspect();
507 }
508
509 for (int i = 0; i < methodDescriptors.length; i++) {
510 if (methodName.equals(methodDescriptors[i].getName())) {
511 return methodDescriptors[i].getMethod();
512 }
513 }
514
515 return null;
516 }
517
518 protected PropertyDescriptor getPropertyDescriptor(String name) {
519 if (propertyDescriptors == null) {
520 introspect();
521 }
522
523 for (int i = 0; i < propertyDescriptors.length; i++) {
524
525
526 if (name.equals(propertyDescriptors[i].getName())) {
527
528 return propertyDescriptors[i];
529 }
530 }
531
532 return null;
533 }
534
535 public Object getObj() {
536 return obj;
537 }
538
539 Method getRelevantMethod(String name, AggregationType aggregationType) {
540 String cName = capitalizeFirstLetter(name);
541 Method relevantMethod;
542 if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) {
543 relevantMethod = findAdderMethod(cName);
544 } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) {
545 relevantMethod = findSetterMethod(cName);
546 } else {
547 throw new IllegalStateException(aggregationType + " not allowed here");
548 }
549 return relevantMethod;
550 }
551
552 <T extends Annotation> T getAnnotation(String name, Class<T> annonationClass,
553 Method relevantMethod) {
554
555 if (relevantMethod != null) {
556 return relevantMethod.getAnnotation(annonationClass);
557 } else {
558 return null;
559 }
560 }
561
562 Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
563 DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class,
564 relevantMethod);
565 if (defaultClassAnnon != null) {
566 Class defaultClass = defaultClassAnnon.value();
567 return defaultClass;
568 }
569 return null;
570 }
571
572 Class getByConcreteType(String name, Method relevantMethod) {
573
574 Class<?> paramType = getParameterClassForMethod(relevantMethod);
575 if (paramType == null) {
576 return null;
577 }
578
579 boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType);
580 if(isUnequivocallyInstantiable) {
581 return paramType;
582 } else {
583 return null;
584 }
585
586 }
587
588 public Class getClassNameViaImplicitRules(String name,
589 AggregationType aggregationType, DefaultNestedComponentRegistry registry) {
590
591 Class registryResult = registry.findDefaultComponentType(obj.getClass(), name);
592 if(registryResult!= null) {
593 return registryResult;
594 }
595
596 Method relevantMethod = getRelevantMethod(name, aggregationType);
597 if (relevantMethod == null) {
598 return null;
599 }
600 Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
601 if (byAnnotation != null) {
602 return byAnnotation;
603 }
604 return getByConcreteType(name, relevantMethod);
605 }
606
607 }