View Javadoc

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