001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.activemq.tool.properties; 018 019 import java.lang.reflect.Constructor; 020 import java.lang.reflect.InvocationTargetException; 021 import java.lang.reflect.Method; 022 import java.util.ArrayList; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Properties; 026 import java.util.StringTokenizer; 027 028 import org.apache.commons.logging.Log; 029 import org.apache.commons.logging.LogFactory; 030 031 public final class ReflectionUtil { 032 private static final Log LOG = LogFactory.getLog(ReflectionUtil.class); 033 034 private ReflectionUtil() { 035 } 036 037 public static void configureClass(Object obj, String key, String val) { 038 try { 039 String debugInfo; 040 041 Object target = obj; 042 Class targetClass = obj.getClass(); 043 044 // DEBUG: Debugging Info 045 debugInfo = "Invoking: " + targetClass.getName(); 046 047 StringTokenizer tokenizer = new StringTokenizer(key, "."); 048 String keySubString = key; 049 int tokenCount = tokenizer.countTokens(); 050 051 // For nested settings, get the object first. -1, do not count the 052 // last token 053 for (int j = 0; j < tokenCount - 1; j++) { 054 // Find getter method first 055 String name = tokenizer.nextToken(); 056 057 // Check if the target object will accept the settings 058 if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(keySubString, val)) { 059 return; 060 } else { 061 // This will reduce the key, so that it will be recognize by 062 // the next object. i.e. 063 // Property name: factory.prefetchPolicy.queuePrefetch 064 // Calling order: 065 // this.getFactory().prefetchPolicy().queuePrefetch(); 066 // If factory does not accept the config, it should be given 067 // prefetchPolicy.queuePrefetch as the key 068 // +1 to account for the '.' 069 keySubString = keySubString.substring(name.length() + 1); 070 } 071 072 String getMethod = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); 073 Method method = targetClass.getMethod(getMethod, new Class[] {}); 074 target = method.invoke(target, null); 075 targetClass = target.getClass(); 076 077 debugInfo += "." + getMethod + "()"; 078 } 079 080 // Property name 081 String property = tokenizer.nextToken(); 082 // Check if the target object will accept the settings 083 if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(property, val)) { 084 return; 085 } 086 087 // Find setter method 088 Method setterMethod = findSetterMethod(targetClass, property); 089 090 // Get the first parameter type. This assumes that there is only one 091 // parameter. 092 if (setterMethod == null) { 093 throw new IllegalAccessException("Unable to find appropriate setter method signature for property: " + property); 094 } 095 Class paramType = setterMethod.getParameterTypes()[0]; 096 097 // Set primitive type 098 debugInfo += "." + setterMethod + "(" + paramType.getName() + ": " + val + ")"; 099 if (paramType.isPrimitive()) { 100 if (paramType == Boolean.TYPE) { 101 setterMethod.invoke(target, new Object[] { 102 Boolean.valueOf(val) 103 }); 104 } else if (paramType == Integer.TYPE) { 105 setterMethod.invoke(target, new Object[] { 106 Integer.valueOf(val) 107 }); 108 } else if (paramType == Long.TYPE) { 109 setterMethod.invoke(target, new Object[] { 110 Long.valueOf(val) 111 }); 112 } else if (paramType == Double.TYPE) { 113 setterMethod.invoke(target, new Object[] { 114 Double.valueOf(val) 115 }); 116 } else if (paramType == Float.TYPE) { 117 setterMethod.invoke(target, new Object[] { 118 Float.valueOf(val) 119 }); 120 } else if (paramType == Short.TYPE) { 121 setterMethod.invoke(target, new Object[] { 122 Short.valueOf(val) 123 }); 124 } else if (paramType == Byte.TYPE) { 125 setterMethod.invoke(target, new Object[] { 126 Byte.valueOf(val) 127 }); 128 } else if (paramType == Character.TYPE) { 129 setterMethod.invoke(target, new Object[] { 130 new Character(val.charAt(0)) 131 }); 132 } 133 } else { 134 // Set String type 135 if (paramType == String.class) { 136 setterMethod.invoke(target, new Object[] { 137 val 138 }); 139 140 // For unknown object type, try to create an instance of the 141 // object using a String constructor 142 } else { 143 Constructor c = paramType.getConstructor(new Class[] { 144 String.class 145 }); 146 Object paramObject = c.newInstance(new Object[] { 147 val 148 }); 149 150 setterMethod.invoke(target, new Object[] { 151 paramObject 152 }); 153 } 154 } 155 LOG.debug(debugInfo); 156 157 } catch (Exception e) { 158 LOG.warn(e); 159 } 160 } 161 162 public static void configureClass(Object obj, Properties props) { 163 for (Iterator i = props.keySet().iterator(); i.hasNext();) { 164 try { 165 String key = (String)i.next(); 166 String val = props.getProperty(key); 167 168 configureClass(obj, key, val); 169 } catch (Throwable t) { 170 // Let's catch any exception as this could be cause by the 171 // foreign class 172 t.printStackTrace(); 173 } 174 } 175 } 176 177 public static Properties retrieveObjectProperties(Object obj) { 178 Properties props = new Properties(); 179 try { 180 props.putAll(retrieveClassProperties("", obj.getClass(), obj)); 181 } catch (Exception e) { 182 LOG.warn(e); 183 } 184 return props; 185 } 186 187 protected static Properties retrieveClassProperties(String prefix, Class targetClass, Object targetObject) { 188 if (targetClass == null || targetObject == null) { 189 return new Properties(); 190 } else { 191 Properties props = new Properties(); 192 Method[] getterMethods = findAllGetterMethods(targetClass); 193 for (int i = 0; i < getterMethods.length; i++) { 194 try { 195 String propertyName = getPropertyName(getterMethods[i].getName()); 196 Class retType = getterMethods[i].getReturnType(); 197 198 // If primitive or string type, return it 199 if (retType.isPrimitive() || retType == String.class) { 200 // Check for an appropriate setter method to consider it 201 // as a property 202 if (findSetterMethod(targetClass, propertyName) != null) { 203 Object val = null; 204 try { 205 val = getterMethods[i].invoke(targetObject, null); 206 } catch (InvocationTargetException e) { 207 e.printStackTrace(); 208 } catch (IllegalAccessException e) { 209 e.printStackTrace(); 210 } 211 props.setProperty(prefix + propertyName, val + ""); 212 } 213 } else { 214 try { 215 Object val = getterMethods[i].invoke(targetObject, null); 216 if (val != null) { 217 props.putAll(retrieveClassProperties(propertyName + ".", val.getClass(), val)); 218 } 219 } catch (InvocationTargetException e) { 220 e.printStackTrace(); 221 } catch (IllegalAccessException e) { 222 e.printStackTrace(); 223 } 224 } 225 } catch (Throwable t) { 226 // Let's catch any exception, cause this could be cause by 227 // the foreign class 228 t.printStackTrace(); 229 } 230 } 231 return props; 232 } 233 } 234 235 private static Method findSetterMethod(Class targetClass, String propertyName) { 236 String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); 237 238 Method[] methods = targetClass.getMethods(); 239 for (int i = 0; i < methods.length; i++) { 240 if (methods[i].getName().equals(methodName) && isSetterMethod(methods[i])) { 241 return methods[i]; 242 } 243 } 244 return null; 245 } 246 247 private static Method findGetterMethod(Class targetClass, String propertyName) { 248 String methodName1 = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); 249 String methodName2 = "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); 250 251 Method[] methods = targetClass.getMethods(); 252 for (int i = 0; i < methods.length; i++) { 253 if ((methods[i].getName().equals(methodName1) || methods[i].getName().equals(methodName2)) && isGetterMethod(methods[i])) { 254 return methods[i]; 255 } 256 } 257 return null; 258 } 259 260 private static Method[] findAllGetterMethods(Class targetClass) { 261 List getterMethods = new ArrayList(); 262 Method[] methods = targetClass.getMethods(); 263 264 for (int i = 0; i < methods.length; i++) { 265 if (isGetterMethod(methods[i])) { 266 getterMethods.add(methods[i]); 267 } 268 } 269 270 return (Method[])getterMethods.toArray(new Method[] {}); 271 } 272 273 private static boolean isGetterMethod(Method method) { 274 // Check method signature first 275 // If 'get' method, must return a non-void value 276 // If 'is' method, must return a boolean value 277 // Both must have no parameters 278 // Method must not belong to the Object class to prevent infinite loop 279 return ((method.getName().startsWith("is") && method.getReturnType() == Boolean.TYPE) || (method.getName().startsWith("get") && method.getReturnType() != Void.TYPE)) 280 && (method.getParameterTypes().length == 0) && method.getDeclaringClass() != Object.class; 281 } 282 283 private static boolean isSetterMethod(Method method) { 284 // Check method signature first 285 if (method.getName().startsWith("set") && method.getReturnType() == Void.TYPE) { 286 Class[] paramType = method.getParameterTypes(); 287 // Check that it can only accept one parameter 288 if (paramType.length == 1) { 289 // Check if parameter is a primitive or can accept a String 290 // parameter 291 if (paramType[0].isPrimitive() || paramType[0] == String.class) { 292 return true; 293 } else { 294 // Check if object can accept a string as a constructor 295 try { 296 if (paramType[0].getConstructor(new Class[] { 297 String.class 298 }) != null) { 299 return true; 300 } 301 } catch (NoSuchMethodException e) { 302 // Do nothing 303 } 304 } 305 } 306 } 307 return false; 308 } 309 310 private static String getPropertyName(String methodName) { 311 String name; 312 if (methodName.startsWith("get")) { 313 name = methodName.substring(3); 314 } else if (methodName.startsWith("set")) { 315 name = methodName.substring(3); 316 } else if (methodName.startsWith("is")) { 317 name = methodName.substring(2); 318 } else { 319 name = ""; 320 } 321 322 return name.substring(0, 1).toLowerCase() + name.substring(1); 323 } 324 }