View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  // Contributors:  Georg Lundesgaard
    19
    20  package org.apache.log4j.config;
    21
    22  import java.beans.Introspector;
    23  import java.beans.PropertyDescriptor;
    24  import java.beans.BeanInfo;
    25  import java.beans.IntrospectionException;
    26  import java.lang.reflect.*;
    27  import java.util.*;
    28  import org.apache.log4j.*;
    29  import org.apache.log4j.helpers.LogLog;
    30  import org.apache.log4j.helpers.OptionConverter;
    31  import org.apache.log4j.spi.OptionHandler;
    32
    33  /***
    34     General purpose Object property setter. Clients repeatedly invokes
    35     {@link #setProperty setProperty(name,value)} in order to invoke setters
    36     on the Object specified in the constructor. This class relies on the
    37     JavaBeans {@link Introspector} to analyze the given Object Class using
    38     reflection.
    39
    40     <p>Usage:
    41     <pre>
    42       PropertySetter ps = new PropertySetter(anObject);
    43       ps.set("name", "Joe");
    44       ps.set("age", "32");
    45       ps.set("isMale", "true");
    46     </pre>
    47     will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
    48     and setMale(true) if such methods exist with those signatures.
    49     Otherwise an {@link IntrospectionException} are thrown.
    50
    51     @author Anders Kristensen
    52     @since 1.1
    53   */
    54  public class PropertySetter {
    55    protected Object obj;
    56    protected PropertyDescriptor[] props;
    57
    58    /***
    59      Create a new PropertySetter for the specified Object. This is done
    60      in prepartion for invoking {@link #setProperty} one or more times.
    61
    62      @param obj  the object for which to set properties
    63     */
    64    public
    65    PropertySetter(Object obj) {
    66      this.obj = obj;
    67    }
    68
    69    /***
    70       Uses JavaBeans {@link Introspector} to computer setters of object to be
    71       configured.
    72     */
    73    protected
    74    void introspect() {
    75      try {
    76        BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
    77        props = bi.getPropertyDescriptors();
    78      } catch (IntrospectionException ex) {
    79        LogLog.error("Failed to introspect "+obj+": " + ex.getMessage());
    80        props = new PropertyDescriptor[0];
    81      }
    82    }
    83
    84
    85    /***
    86       Set the properties of an object passed as a parameter in one
    87       go. The <code>properties</code> are parsed relative to a
    88       <code>prefix</code>.
    89
    90       @param obj The object to configure.
    91       @param properties A java.util.Properties containing keys and values.
    92       @param prefix Only keys having the specified prefix will be set.
    93    */
    94    public
    95    static
    96    void setProperties(Object obj, Properties properties, String prefix) {
    97      new PropertySetter(obj).setProperties(properties, prefix);
    98    }
    99
    100
    101   /***
    102      Set the properites for the object that match the
    103      <code>prefix</code> passed as parameter.
    104
    105
    106    */
    107   public
    108   void setProperties(Properties properties, String prefix) {
    109     int len = prefix.length();
    110
    111     for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
    112       String key = (String) e.nextElement();
    113
    114       // handle only properties that start with the desired frefix.
    115       if (key.startsWith(prefix)) {
    116
    117
    118 	// ignore key if it contains dots after the prefix
    119         if (key.indexOf('.', len + 1) > 0) {
    120 	  //System.err.println("----------Ignoring---["+key
    121 	  //	     +"], prefix=["+prefix+"].");
    122 	  continue;
    123 	}
    124
    125 	String value = OptionConverter.findAndSubst(key, properties);
    126         key = key.substring(len);
    127         if ("layout".equals(key) && obj instanceof Appender) {
    128           continue;
    129         }
    130         setProperty(key, value);
    131       }
    132     }
    133     activate();
    134   }
    135
    136   /***
    137      Set a property on this PropertySetter's Object. If successful, this
    138      method will invoke a setter method on the underlying Object. The
    139      setter is the one for the specified property name and the value is
    140      determined partly from the setter argument type and partly from the
    141      value specified in the call to this method.
    142
    143      <p>If the setter expects a String no conversion is necessary.
    144      If it expects an int, then an attempt is made to convert 'value'
    145      to an int using new Integer(value). If the setter expects a boolean,
    146      the conversion is by new Boolean(value).
    147
    148      @param name    name of the property
    149      @param value   String value of the property
    150    */
    151   public
    152   void setProperty(String name, String value) {
    153     if (value == null) return;
    154
    155     name = Introspector.decapitalize(name);
    156     PropertyDescriptor prop = getPropertyDescriptor(name);
    157
    158     //LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType());
    159
    160     if (prop == null) {
    161       LogLog.warn("No such property [" + name + "] in "+
    162 		  obj.getClass().getName()+"." );
    163     } else {
    164       try {
    165         setProperty(prop, name, value);
    166       } catch (PropertySetterException ex) {
    167         LogLog.warn("Failed to set property [" + name +
    168                     "] to value \"" + value + "\". ", ex.rootCause);
    169       }
    170     }
    171   }
    172
    173   /***
    174       Set the named property given a {@link PropertyDescriptor}.
    175
    176       @param prop A PropertyDescriptor describing the characteristics
    177       of the property to set.
    178       @param name The named of the property to set.
    179       @param value The value of the property.
    180    */
    181   public
    182   void setProperty(PropertyDescriptor prop, String name, String value)
    183     throws PropertySetterException {
    184     Method setter = prop.getWriteMethod();
    185     if (setter == null) {
    186       throw new PropertySetterException("No setter for property ["+name+"].");
    187     }
    188     Class[] paramTypes = setter.getParameterTypes();
    189     if (paramTypes.length != 1) {
    190       throw new PropertySetterException("#params for setter != 1");
    191     }
    192
    193     Object arg;
    194     try {
    195       arg = convertArg(value, paramTypes[0]);
    196     } catch (Throwable t) {
    197       throw new PropertySetterException("Conversion to type ["+paramTypes[0]+
    198 					"] failed. Reason: "+t);
    199     }
    200     if (arg == null) {
    201       throw new PropertySetterException(
    202           "Conversion to type ["+paramTypes[0]+"] failed.");
    203     }
    204     LogLog.debug("Setting property [" + name + "] to [" +arg+"].");
    205     try {
    206       setter.invoke(obj, new Object[]  { arg });
    207     } catch (Exception ex) {
    208       throw new PropertySetterException(ex);
    209     }
    210   }
    211
    212
    213   /***
    214      Convert <code>val</code> a String parameter to an object of a
    215      given type.
    216   */
    217   protected
    218   Object convertArg(String val, Class type) {
    219     if(val == null)
    220       return null;
    221
    222     String v = val.trim();
    223     if (String.class.isAssignableFrom(type)) {
    224       return val;
    225     } else if (Integer.TYPE.isAssignableFrom(type)) {
    226       return new Integer(v);
    227     } else if (Long.TYPE.isAssignableFrom(type)) {
    228       return new Long(v);
    229     } else if (Boolean.TYPE.isAssignableFrom(type)) {
    230       if ("true".equalsIgnoreCase(v)) {
    231         return Boolean.TRUE;
    232       } else if ("false".equalsIgnoreCase(v)) {
    233         return Boolean.FALSE;
    234       }
    235     } else if (Priority.class.isAssignableFrom(type)) {
    236       return OptionConverter.toLevel(v, (Level) Level.DEBUG);
    237     }
    238     return null;
    239   }
    240
    241
    242   protected
    243   PropertyDescriptor getPropertyDescriptor(String name) {
    244     if (props == null) introspect();
    245
    246     for (int i = 0; i < props.length; i++) {
    247       if (name.equals(props[i].getName())) {
    248 	return props[i];
    249       }
    250     }
    251     return null;
    252   }
    253
    254   public
    255   void activate() {
    256     if (obj instanceof OptionHandler) {
    257       ((OptionHandler) obj).activateOptions();
    258     }
    259   }
    260 }