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 org.apache.log4j.Appender;
    23  import org.apache.log4j.Level;
    24  import org.apache.log4j.Priority;
    25  import org.apache.log4j.helpers.LogLog;
    26  import org.apache.log4j.helpers.OptionConverter;
    27  import org.apache.log4j.spi.OptionHandler;
    28  import org.apache.log4j.spi.ErrorHandler;
    29
    30  import java.beans.BeanInfo;
    31  import java.beans.IntrospectionException;
    32  import java.beans.Introspector;
    33  import java.beans.PropertyDescriptor;
    34  import java.io.InterruptedIOException;
    35  import java.lang.reflect.InvocationTargetException;
    36  import java.lang.reflect.Method;
    37  import java.util.Enumeration;
    38  import java.util.Properties;
    39
    40  /**
    41     General purpose Object property setter. Clients repeatedly invokes
    42     {@link #setProperty setProperty(name,value)} in order to invoke setters
    43     on the Object specified in the constructor. This class relies on the
    44     JavaBeans {@link Introspector} to analyze the given Object Class using
    45     reflection.
    46
    47     <p>Usage:
    48     <pre>
    49       PropertySetter ps = new PropertySetter(anObject);
    50       ps.set("name", "Joe");
    51       ps.set("age", "32");
    52       ps.set("isMale", "true");
    53     </pre>
    54     will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
    55     and setMale(true) if such methods exist with those signatures.
    56     Otherwise an {@link IntrospectionException} are thrown.
    57
    58     @author Anders Kristensen
    59     @since 1.1
    60   */
    61  public class PropertySetter {
    62    protected Object obj;
    63    protected PropertyDescriptor[] props;
    64
    65    /**
    66      Create a new PropertySetter for the specified Object. This is done
    67      in prepartion for invoking {@link #setProperty} one or more times.
    68
    69      @param obj  the object for which to set properties
    70     */
    71    public
    72    PropertySetter(Object obj) {
    73      this.obj = obj;
    74    }
    75
    76    /**
    77       Uses JavaBeans {@link Introspector} to computer setters of object to be
    78       configured.
    79     */
    80    protected
    81    void introspect() {
    82      try {
    83        BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
    84        props = bi.getPropertyDescriptors();
    85      } catch (IntrospectionException ex) {
    86        LogLog.error("Failed to introspect "+obj+": " + ex.getMessage());
    87        props = new PropertyDescriptor[0];
    88      }
    89    }
    90
    91
    92    /**
    93       Set the properties of an object passed as a parameter in one
    94       go. The <code>properties</code> are parsed relative to a
    95       <code>prefix</code>.
    96
    97       @param obj The object to configure.
    98       @param properties A java.util.Properties containing keys and values.
    99       @param prefix Only keys having the specified prefix will be set.
    100   */
    101   public
    102   static
    103   void setProperties(Object obj, Properties properties, String prefix) {
    104     new PropertySetter(obj).setProperties(properties, prefix);
    105   }
    106
    107
    108   /**
    109      Set the properites for the object that match the
    110      <code>prefix</code> passed as parameter.
    111
    112
    113    */
    114   public
    115   void setProperties(Properties properties, String prefix) {
    116     int len = prefix.length();
    117
    118     for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
    119       String key = (String) e.nextElement();
    120
    121       // handle only properties that start with the desired frefix.
    122       if (key.startsWith(prefix)) {
    123
    124
    125 	// ignore key if it contains dots after the prefix
    126         if (key.indexOf('.', len + 1) > 0) {
    127 	  //System.err.println("----------Ignoring---["+key
    128 	  //	     +"], prefix=["+prefix+"].");
    129 	  continue;
    130 	}
    131
    132 	String value = OptionConverter.findAndSubst(key, properties);
    133         key = key.substring(len);
    134         if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
    135           continue;
    136         }
    137         //
    138         //   if the property type is an OptionHandler
    139         //     (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
    140         PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
    141         if (prop != null
    142                 && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
    143                 && prop.getWriteMethod() != null) {
    144             OptionHandler opt = (OptionHandler)
    145                     OptionConverter.instantiateByKey(properties, prefix + key,
    146                                   prop.getPropertyType(),
    147                                   null);
    148             PropertySetter setter = new PropertySetter(opt);
    149             setter.setProperties(properties, prefix + key + ".");
    150             try {
    151                 prop.getWriteMethod().invoke(this.obj, new Object[] { opt });
    152             } catch(IllegalAccessException ex) {
    153                 LogLog.warn("Failed to set property [" + key +
    154                             "] to value \"" + value + "\". ", ex);
    155             } catch(InvocationTargetException ex) {
    156                 if (ex.getTargetException() instanceof InterruptedException
    157                         || ex.getTargetException() instanceof InterruptedIOException) {
    158                     Thread.currentThread().interrupt();
    159                 }
    160                 LogLog.warn("Failed to set property [" + key +
    161                             "] to value \"" + value + "\". ", ex);
    162             } catch(RuntimeException ex) {
    163                 LogLog.warn("Failed to set property [" + key +
    164                             "] to value \"" + value + "\". ", ex);
    165             }
    166             continue;
    167         }
    168
    169         setProperty(key, value);
    170       }
    171     }
    172     activate();
    173   }
    174
    175   /**
    176      Set a property on this PropertySetter's Object. If successful, this
    177      method will invoke a setter method on the underlying Object. The
    178      setter is the one for the specified property name and the value is
    179      determined partly from the setter argument type and partly from the
    180      value specified in the call to this method.
    181
    182      <p>If the setter expects a String no conversion is necessary.
    183      If it expects an int, then an attempt is made to convert 'value'
    184      to an int using new Integer(value). If the setter expects a boolean,
    185      the conversion is by new Boolean(value).
    186
    187      @param name    name of the property
    188      @param value   String value of the property
    189    */
    190   public
    191   void setProperty(String name, String value) {
    192     if (value == null) return;
    193
    194     name = Introspector.decapitalize(name);
    195     PropertyDescriptor prop = getPropertyDescriptor(name);
    196
    197     //LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType());
    198
    199     if (prop == null) {
    200       LogLog.warn("No such property [" + name + "] in "+
    201 		  obj.getClass().getName()+"." );
    202     } else {
    203       try {
    204         setProperty(prop, name, value);
    205       } catch (PropertySetterException ex) {
    206         LogLog.warn("Failed to set property [" + name +
    207                     "] to value \"" + value + "\". ", ex.rootCause);
    208       }
    209     }
    210   }
    211
    212   /**
    213       Set the named property given a {@link PropertyDescriptor}.
    214
    215       @param prop A PropertyDescriptor describing the characteristics
    216       of the property to set.
    217       @param name The named of the property to set.
    218       @param value The value of the property.
    219    */
    220   public
    221   void setProperty(PropertyDescriptor prop, String name, String value)
    222     throws PropertySetterException {
    223     Method setter = prop.getWriteMethod();
    224     if (setter == null) {
    225       throw new PropertySetterException("No setter for property ["+name+"].");
    226     }
    227     Class[] paramTypes = setter.getParameterTypes();
    228     if (paramTypes.length != 1) {
    229       throw new PropertySetterException("#params for setter != 1");
    230     }
    231
    232     Object arg;
    233     try {
    234       arg = convertArg(value, paramTypes[0]);
    235     } catch (Throwable t) {
    236       throw new PropertySetterException("Conversion to type ["+paramTypes[0]+
    237 					"] failed. Reason: "+t);
    238     }
    239     if (arg == null) {
    240       throw new PropertySetterException(
    241           "Conversion to type ["+paramTypes[0]+"] failed.");
    242     }
    243     LogLog.debug("Setting property [" + name + "] to [" +arg+"].");
    244     try {
    245       setter.invoke(obj, new Object[]  { arg });
    246     } catch (IllegalAccessException ex) {
    247       throw new PropertySetterException(ex);
    248     } catch (InvocationTargetException ex) {
    249         if (ex.getTargetException() instanceof InterruptedException
    250                 || ex.getTargetException() instanceof InterruptedIOException) {
    251             Thread.currentThread().interrupt();
    252         }
    253         throw new PropertySetterException(ex);
    254     } catch (RuntimeException ex) {
    255       throw new PropertySetterException(ex);
    256     }
    257   }
    258
    259
    260   /**
    261      Convert <code>val</code> a String parameter to an object of a
    262      given type.
    263   */
    264   protected
    265   Object convertArg(String val, Class type) {
    266     if(val == null)
    267       return null;
    268
    269     String v = val.trim();
    270     if (String.class.isAssignableFrom(type)) {
    271       return val;
    272     } else if (Integer.TYPE.isAssignableFrom(type)) {
    273       return new Integer(v);
    274     } else if (Long.TYPE.isAssignableFrom(type)) {
    275       return new Long(v);
    276     } else if (Boolean.TYPE.isAssignableFrom(type)) {
    277       if ("true".equalsIgnoreCase(v)) {
    278         return Boolean.TRUE;
    279       } else if ("false".equalsIgnoreCase(v)) {
    280         return Boolean.FALSE;
    281       }
    282     } else if (Priority.class.isAssignableFrom(type)) {
    283       return OptionConverter.toLevel(v, (Level) Level.DEBUG);
    284     } else if (ErrorHandler.class.isAssignableFrom(type)) {
    285       return OptionConverter.instantiateByClassName(v,
    286 	  ErrorHandler.class, null);
    287     }
    288     return null;
    289   }
    290
    291
    292   protected
    293   PropertyDescriptor getPropertyDescriptor(String name) {
    294     if (props == null) introspect();
    295
    296     for (int i = 0; i < props.length; i++) {
    297       if (name.equals(props[i].getName())) {
    298 	return props[i];
    299       }
    300     }
    301     return null;
    302   }
    303
    304   public
    305   void activate() {
    306     if (obj instanceof OptionHandler) {
    307       ((OptionHandler) obj).activateOptions();
    308     }
    309   }
    310 }