/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.tool.properties;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ReflectionUtil {
private static final Logger LOG = LoggerFactory.getLogger(ReflectionUtil.class);
private ReflectionUtil() {
}
public static void configureClass(Object obj, String key, String val) {
try {
String debugInfo;
Object target = obj;
Class<?> targetClass = obj.getClass();
// DEBUG: Debugging Info
debugInfo = "Invoking: " + targetClass.getName();
StringTokenizer tokenizer = new StringTokenizer(key, ".");
String keySubString = key;
int tokenCount = tokenizer.countTokens();
// For nested settings, get the object first. -1, do not count the
// last token
for (int j = 0; j < tokenCount - 1; j++) {
// Find getter method first
String name = tokenizer.nextToken();
// Check if the target object will accept the settings
if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(keySubString, val)) {
return;
} else {
// This will reduce the key, so that it will be recognize by
// the next object. i.e.
// Property name: factory.prefetchPolicy.queuePrefetch
// Calling order:
// this.getFactory().prefetchPolicy().queuePrefetch();
// If factory does not accept the config, it should be given
// prefetchPolicy.queuePrefetch as the key
// +1 to account for the '.'
keySubString = keySubString.substring(name.length() + 1);
}
String getMethod = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method method = targetClass.getMethod(getMethod, new Class[] {});
target = method.invoke(target, null);
targetClass = target.getClass();
debugInfo += "." + getMethod + "()";
}
// Property name
String property = tokenizer.nextToken();
// Check if the target object will accept the settings
if (target instanceof ReflectionConfigurable && !((ReflectionConfigurable)target).acceptConfig(property, val)) {
return;
}
// Find setter method
Method setterMethod = findSetterMethod(targetClass, property);
// Get the first parameter type. This assumes that there is only one
// parameter.
if (setterMethod == null) {
throw new IllegalAccessException("Unable to find appropriate setter method signature for property: " + property);
}
Class<?> paramType = setterMethod.getParameterTypes()[0];
// Set primitive type
debugInfo += "." + setterMethod + "(" + paramType.getName() + ": " + val + ")";
if (paramType.isPrimitive()) {
if (paramType == Boolean.TYPE) {
setterMethod.invoke(target, new Object[] {
Boolean.valueOf(val)
});
} else if (paramType == Integer.TYPE) {
setterMethod.invoke(target, new Object[] {
Integer.valueOf(val)
});
} else if (paramType == Long.TYPE) {
setterMethod.invoke(target, new Object[] {
Long.valueOf(val)
});
} else if (paramType == Double.TYPE) {
setterMethod.invoke(target, new Object[] {
Double.valueOf(val)
});
} else if (paramType == Float.TYPE) {
setterMethod.invoke(target, new Object[] {
Float.valueOf(val)
});
} else if (paramType == Short.TYPE) {
setterMethod.invoke(target, new Object[] {
Short.valueOf(val)
});
} else if (paramType == Byte.TYPE) {
setterMethod.invoke(target, new Object[] {
Byte.valueOf(val)
});
} else if (paramType == Character.TYPE) {
setterMethod.invoke(target, new Object[] {
new Character(val.charAt(0))
});
}
} else {
// Set String type
if (paramType == String.class) {
setterMethod.invoke(target, new Object[] {
val
});
// For unknown object type, try to create an instance of the
// object using a String constructor
} else {
Constructor c = paramType.getConstructor(new Class[] {
String.class
});
Object paramObject = c.newInstance(new Object[] {
val
});
setterMethod.invoke(target, new Object[] {
paramObject
});
}
}
LOG.debug(debugInfo);
} catch (Exception e) {
LOG.warn(e.toString());
}
}
public static void configureClass(Object obj, Properties props) {
for (Iterator<Object> i = props.keySet().iterator(); i.hasNext();) {
try {
String key = (String)i.next();
String val = props.getProperty(key);
configureClass(obj, key, val);
} catch (Throwable t) {
// Let's catch any exception as this could be cause by the
// foreign class
t.printStackTrace();
}
}
}
public static Properties retrieveObjectProperties(Object obj) {
Properties props = new Properties();
try {
props.putAll(retrieveClassProperties("", obj.getClass(), obj));
} catch (Exception e) {
LOG.warn(e.toString());
}
return props;
}
protected static Properties retrieveClassProperties(String prefix, Class targetClass, Object targetObject) {
if (targetClass == null || targetObject == null) {
return new Properties();
} else {
Properties props = new Properties();
Method[] getterMethods = findAllGetterMethods(targetClass);
for (int i = 0; i < getterMethods.length; i++) {
try {
String propertyName = getPropertyName(getterMethods[i].getName());
Class retType = getterMethods[i].getReturnType();
// If primitive or string type, return it
if (retType.isPrimitive() || retType == String.class) {
// Check for an appropriate setter method to consider it
// as a property
if (findSetterMethod(targetClass, propertyName) != null) {
Object val = null;
try {
val = getterMethods[i].invoke(targetObject, null);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
props.setProperty(prefix + propertyName, val + "");
}
} else {
try {
Object val = getterMethods[i].invoke(targetObject, null);
if (val != null && val != targetObject) {
props.putAll(retrieveClassProperties(propertyName + ".", val.getClass(), val));
}
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} catch (Throwable t) {
// Let's catch any exception, cause this could be cause by
// the foreign class
t.printStackTrace();
}
}
return props;
}
}
private static Method findSetterMethod(Class targetClass, String propertyName) {
String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
Method[] methods = targetClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && isSetterMethod(methods[i])) {
return methods[i];
}
}
return null;
}
private static Method findGetterMethod(Class targetClass, String propertyName) {
String methodName1 = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
String methodName2 = "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
Method[] methods = targetClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if ((methods[i].getName().equals(methodName1) || methods[i].getName().equals(methodName2)) && isGetterMethod(methods[i])) {
return methods[i];
}
}
return null;
}
private static Method[] findAllGetterMethods(Class targetClass) {
List getterMethods = new ArrayList();
Method[] methods = targetClass.getMethods();
for (int i = 0; i < methods.length; i++) {
if (isGetterMethod(methods[i])) {
getterMethods.add(methods[i]);
}
}
return (Method[])getterMethods.toArray(new Method[] {});
}
private static boolean isGetterMethod(Method method) {
// Check method signature first
// If 'get' method, must return a non-void value
// If 'is' method, must return a boolean value
// Both must have no parameters
// Method must not belong to the Object class to prevent infinite loop
return ((method.getName().startsWith("is") && method.getReturnType() == Boolean.TYPE) || (method.getName().startsWith("get") && method.getReturnType() != Void.TYPE))
&& (method.getParameterTypes().length == 0) && method.getDeclaringClass() != Object.class;
}
private static boolean isSetterMethod(Method method) {
// Check method signature first
if (method.getName().startsWith("set") && method.getReturnType() == Void.TYPE) {
Class[] paramType = method.getParameterTypes();
// Check that it can only accept one parameter
if (paramType.length == 1) {
// Check if parameter is a primitive or can accept a String
// parameter
if (paramType[0].isPrimitive() || paramType[0] == String.class) {
return true;
} else {
// Check if object can accept a string as a constructor
try {
if (paramType[0].getConstructor(new Class[] {
String.class
}) != null) {
return true;
}
} catch (NoSuchMethodException e) {
// Do nothing
}
}
}
}
return false;
}
private static String getPropertyName(String methodName) {
String name;
if (methodName.startsWith("get")) {
name = methodName.substring(3);
} else if (methodName.startsWith("set")) {
name = methodName.substring(3);
} else if (methodName.startsWith("is")) {
name = methodName.substring(2);
} else {
name = "";
}
return name.substring(0, 1).toLowerCase() + name.substring(1);
}
}