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( "name", "Joe" );
48 * ps.set( "age", "32" );
49 * ps.set( "isMale", "true" );
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 }