org.apache.log4j.config.PropertySetter Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Contributors: Georg Lundesgaard
package org.apache.log4j.config;
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.OptionHandler;
import org.apache.log4j.spi.ErrorHandler;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InterruptedIOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Properties;
/**
* General purpose Object property setter. Clients repeatedly invokes
* {@link #setProperty setProperty(name,value)} in order to invoke setters on
* the Object specified in the constructor. This class relies on the JavaBeans
* {@link Introspector} to analyze the given Object Class using reflection.
*
*
* Usage:
*
*
* PropertySetter ps = new PropertySetter(anObject);
* ps.set("name", "Joe");
* ps.set("age", "32");
* ps.set("isMale", "true");
*
*
* will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and
* setMale(true) if such methods exist with those signatures. Otherwise an
* {@link IntrospectionException} are thrown.
*
* @author Anders Kristensen
* @since 1.1
*/
public class PropertySetter {
protected Object obj;
protected PropertyDescriptor[] props;
/**
* Create a new PropertySetter for the specified Object. This is done in
* prepartion for invoking {@link #setProperty} one or more times.
*
* @param obj the object for which to set properties
*/
public PropertySetter(Object obj) {
this.obj = obj;
}
/**
* Uses JavaBeans {@link Introspector} to computer setters of object to be
* configured.
*/
protected void introspect() {
try {
BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
props = bi.getPropertyDescriptors();
} catch (IntrospectionException ex) {
LogLog.error("Failed to introspect " + obj + ": " + ex.getMessage());
props = new PropertyDescriptor[0];
}
}
/**
* Set the properties of an object passed as a parameter in one go. The
* properties
are parsed relative to a prefix
.
*
* @param obj The object to configure.
* @param properties A java.util.Properties containing keys and values.
* @param prefix Only keys having the specified prefix will be set.
*/
public static void setProperties(Object obj, Properties properties, String prefix) {
new PropertySetter(obj).setProperties(properties, prefix);
}
/**
* Set the properites for the object that match the prefix
passed
* as parameter.
*
*
*/
public void setProperties(Properties properties, String prefix) {
int len = prefix.length();
for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
String key = (String) e.nextElement();
// handle only properties that start with the desired frefix.
if (key.startsWith(prefix)) {
// ignore key if it contains dots after the prefix
if (key.indexOf('.', len + 1) > 0) {
// System.err.println("----------Ignoring---["+key
// +"], prefix=["+prefix+"].");
continue;
}
String value = OptionConverter.findAndSubst(key, properties);
key = key.substring(len);
if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
continue;
}
//
// if the property type is an OptionHandler
// (for example, triggeringPolicy of
// org.apache.log4j.rolling.RollingFileAppender)
PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
if (prop != null && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
&& prop.getWriteMethod() != null) {
OptionHandler opt = (OptionHandler) OptionConverter.instantiateByKey(properties, prefix + key,
prop.getPropertyType(), null);
PropertySetter setter = new PropertySetter(opt);
setter.setProperties(properties, prefix + key + ".");
try {
prop.getWriteMethod().invoke(this.obj, new Object[] { opt });
} catch (IllegalAccessException ex) {
LogLog.warn("Failed to set property [" + key + "] to value \"" + value + "\". ", ex);
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof InterruptedException
|| ex.getTargetException() instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.warn("Failed to set property [" + key + "] to value \"" + value + "\". ", ex);
} catch (RuntimeException ex) {
LogLog.warn("Failed to set property [" + key + "] to value \"" + value + "\". ", ex);
}
continue;
}
setProperty(key, value);
}
}
activate();
}
/**
* Set a property on this PropertySetter's Object. If successful, this method
* will invoke a setter method on the underlying Object. The setter is the one
* for the specified property name and the value is determined partly from the
* setter argument type and partly from the value specified in the call to this
* method.
*
*
* If the setter expects a String no conversion is necessary. If it expects an
* int, then an attempt is made to convert 'value' to an int using new
* Integer(value). If the setter expects a boolean, the conversion is by new
* Boolean(value).
*
* @param name name of the property
* @param value String value of the property
*/
public void setProperty(String name, String value) {
if (value == null)
return;
name = Introspector.decapitalize(name);
PropertyDescriptor prop = getPropertyDescriptor(name);
// LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType());
if (prop == null) {
LogLog.warn("No such property [" + name + "] in " + obj.getClass().getName() + ".");
} else {
try {
setProperty(prop, name, value);
} catch (PropertySetterException ex) {
LogLog.warn("Failed to set property [" + name + "] to value \"" + value + "\". ", ex.rootCause);
}
}
}
/**
* Set the named property given a {@link PropertyDescriptor}.
*
* @param prop A PropertyDescriptor describing the characteristics of the
* property to set.
* @param name The named of the property to set.
* @param value The value of the property.
*/
public void setProperty(PropertyDescriptor prop, String name, String value) throws PropertySetterException {
Method setter = prop.getWriteMethod();
if (setter == null) {
throw new PropertySetterException("No setter for property [" + name + "].");
}
Class[] paramTypes = setter.getParameterTypes();
if (paramTypes.length != 1) {
throw new PropertySetterException("#params for setter != 1");
}
Object arg;
try {
arg = convertArg(value, paramTypes[0]);
} catch (Throwable t) {
throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed. Reason: " + t);
}
if (arg == null) {
throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed.");
}
LogLog.debug("Setting property [" + name + "] to [" + arg + "].");
try {
setter.invoke(obj, new Object[] { arg });
} catch (IllegalAccessException ex) {
throw new PropertySetterException(ex);
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof InterruptedException
|| ex.getTargetException() instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
throw new PropertySetterException(ex);
} catch (RuntimeException ex) {
throw new PropertySetterException(ex);
}
}
/**
* Convert val
a String parameter to an object of a given type.
*/
protected Object convertArg(String val, Class type) {
if (val == null)
return null;
String v = val.trim();
if (String.class.isAssignableFrom(type)) {
return val;
} else if (Integer.TYPE.isAssignableFrom(type)) {
return new Integer(v);
} else if (Long.TYPE.isAssignableFrom(type)) {
return new Long(v);
} else if (Boolean.TYPE.isAssignableFrom(type)) {
if ("true".equalsIgnoreCase(v)) {
return Boolean.TRUE;
} else if ("false".equalsIgnoreCase(v)) {
return Boolean.FALSE;
}
} else if (Priority.class.isAssignableFrom(type)) {
return OptionConverter.toLevel(v, (Level) Level.DEBUG);
} else if (ErrorHandler.class.isAssignableFrom(type)) {
return OptionConverter.instantiateByClassName(v, ErrorHandler.class, null);
}
return null;
}
protected PropertyDescriptor getPropertyDescriptor(String name) {
if (props == null)
introspect();
for (int i = 0; i < props.length; i++) {
if (name.equals(props[i].getName())) {
return props[i];
}
}
return null;
}
public void activate() {
if (obj instanceof OptionHandler) {
((OptionHandler) obj).activateOptions();
}
}
}