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