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 }