001package org.apache.commons.jcs.utils.config; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import java.beans.BeanInfo; 026import java.beans.IntrospectionException; 027import java.beans.Introspector; 028import java.beans.PropertyDescriptor; 029import java.io.File; 030import java.lang.reflect.Method; 031import java.util.Enumeration; 032import java.util.Properties; 033 034/** 035 * This class is based on the log4j class org.apache.log4j.config.PropertySetter that was made by 036 * Anders Kristensen 037 * <p> 038 * General purpose Object property setter. Clients repeatedly invokes {@link #setProperty 039 * setProperty(name,value)} in order to invoke setters on the Object specified in the constructor. 040 * This class relies on the JavaBeans {@link Introspector}to analyze the given Object Class using 041 * reflection. 042 * <p> 043 * Usage: 044 * 045 * <pre> 046 * PropertySetter ps = new PropertySetter( anObject ); 047 * ps.set( "name", "Joe" ); 048 * ps.set( "age", "32" ); 049 * ps.set( "isMale", "true" ); 050 * </pre> 051 * 052 * will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and setMale(true) if 053 * such methods exist with those signatures. Otherwise an {@link IntrospectionException}are thrown. 054 */ 055public class PropertySetter 056{ 057 /** Logger */ 058 private static final Log log = LogFactory.getLog( PropertySetter.class ); 059 060 /** Description of the Field */ 061 private final Object obj; 062 063 /** Description of the Field */ 064 private PropertyDescriptor[] props; 065 066 /** 067 * Create a new PropertySetter for the specified Object. This is done in preparation for invoking 068 * {@link #setProperty}one or more times. 069 * @param obj the object for which to set properties 070 */ 071 public PropertySetter( Object obj ) 072 { 073 this.obj = obj; 074 } 075 076 /** 077 * Uses JavaBeans {@link Introspector}to compute setters of object to be configured. 078 */ 079 protected void introspect() 080 { 081 try 082 { 083 BeanInfo bi = Introspector.getBeanInfo( obj.getClass() ); 084 props = bi.getPropertyDescriptors(); 085 } 086 catch ( IntrospectionException ex ) 087 { 088 log.error( "Failed to introspect " + obj + ": " + ex.getMessage() ); 089 props = new PropertyDescriptor[0]; 090 } 091 } 092 093 /** 094 * Set the properties of an object passed as a parameter in one go. The <code>properties</code> 095 * are parsed relative to a <code>prefix</code>. 096 * <p> 097 * @param obj The object to configure. 098 * @param properties A java.util.Properties containing keys and values. 099 * @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}