/*
* 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 hydra;
// import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.apache.geode.SystemFailure;
/**
*
* A class specialized for executing (via reflection) the receiver/selector pairs found in
* TestTasks.
*
*/
public class MethExecutor {
// @todo lises add static args method
/**
* Helper method that searches a class (and its superclasses) for a method with the given name and
* parameter types.
*
* @throws NoSuchMethodException If the method cannot be found
*/
public static Method getMethod(Class c, String methodName, Class[] paramTypes)
throws NoSuchMethodException {
ArrayList matchingMethods = new ArrayList();
for (Class q = c; q != null; q = q.getSuperclass()) {
Method[] methods = q.getDeclaredMethods();
NEXT_METHOD: for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (!m.getName().equals(methodName)) {
continue;
}
Class[] argTypes = m.getParameterTypes();
if (argTypes.length != paramTypes.length) {
continue;
}
for (int j = 0; j < argTypes.length; j++) {
if (paramTypes[j] == null) {
if (argTypes[j].isPrimitive()) {
// this parameter is not ok, the parameter is a primative and the value is null
continue NEXT_METHOD;
} else {
// this parameter is ok, the argument is an object and the value is null
continue;
}
}
if (!argTypes[j].isAssignableFrom(paramTypes[j])) {
Class argType = argTypes[j];
Class paramType = paramTypes[j];
if (argType.isPrimitive()) {
if ((argType.equals(boolean.class) && paramType.equals(Boolean.class))
|| (argType.equals(short.class) && paramType.equals(Short.class))
|| (argType.equals(int.class) && paramType.equals(Integer.class))
|| (argType.equals(long.class) && paramType.equals(Long.class))
|| (argType.equals(float.class) && paramType.equals(Float.class))
|| (argType.equals(double.class) && paramType.equals(Double.class))
|| (argType.equals(char.class) && paramType.equals(Character.class))
|| (argType.equals(byte.class) && paramType.equals(Byte.class)) || false) {
// This parameter is okay, try the next arg
continue;
}
}
continue NEXT_METHOD;
}
}
matchingMethods.add(m);
}
// We want to check to make sure there aren't two
// ambiguous methods on the same class. But a subclass
// can still override a method on a super class, so we'll stop
// if we found a method on the subclass.
if (matchingMethods.size() > 0) {
break;
}
}
if (matchingMethods.isEmpty()) {
StringBuffer sb = new StringBuffer();
sb.append("Could not find method ");
sb.append(methodName);
sb.append(" with ");
sb.append(paramTypes.length);
sb.append(" parameters [");
for (int i = 0; i < paramTypes.length; i++) {
String name = paramTypes[i] == null ? null : paramTypes[i].getName();
sb.append(name);
if (i < paramTypes.length - 1) {
sb.append(", ");
}
}
sb.append("] in class ");
sb.append(c.getName());
throw new NoSuchMethodException(sb.toString());
}
if (matchingMethods.size() > 1) {
StringBuffer sb = new StringBuffer();
sb.append("Method is ambiguous ");
sb.append(methodName);
sb.append(" with ");
sb.append(paramTypes.length);
sb.append(" parameters [");
for (int i = 0; i < paramTypes.length; i++) {
String name = paramTypes[i] == null ? null : paramTypes[i].getName();
sb.append(name);
if (i < paramTypes.length - 1) {
sb.append(", ");
}
}
sb.append("] in class ");
sb.append(c.getName());
sb.append(" methods=" + matchingMethods);
throw new NoSuchMethodException(sb.toString());
} else
return (Method) matchingMethods.get(0);
}
/**
*
* Send the message "selector" to the class named "receiver". Return the result, including stack
* trace (if any).
*
*/
public static MethExecutorResult execute(String receiver, String selector) {
return execute(receiver, selector, null);
}
/**
* Executes the given static method on the given class with the given arguments.
*/
public static MethExecutorResult execute(String receiver, String selector, Object[] args) {
try {
// get the class
Class receiverClass = Class.forName(receiver);
// invoke the method
Object res = null;
try {
Class[] paramTypes;
if (args == null) {
paramTypes = new Class[0];
} else {
paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
paramTypes[i] = null;
} else {
paramTypes[i] = args[i].getClass();
}
}
}
Method theMethod = getMethod(receiverClass, selector, paramTypes);
theMethod.setAccessible(true);
res = theMethod.invoke(receiverClass, args);
return new MethExecutorResult(res);
} catch (InvocationTargetException invTargEx) {
Throwable targEx = invTargEx.getTargetException();
if (targEx == null) {
return new MethExecutorResult(res);
} else {
return new MethExecutorResult(targEx);
}
}
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable t) {
// String s = "While trying to invoke " + receiver + "." +
// selector;
// t = new HydraConfigException(s, t);
return new MethExecutorResult(t);
}
}
/**
*
* Send the message "selector" to the object "target". Return the result, including stack trace
* (if any).
*
*/
public static MethExecutorResult executeObject(Object target, String selector) {
return executeObject(target, selector, null);
}
/**
* Executes the given instance method on the given object with the given arguments.
*/
public static MethExecutorResult executeObject(Object target, String selector, Object[] args) {
try {
// get the class
Class receiverClass = target.getClass();
// invoke the method
Object res = null;
try {
Class[] paramTypes;
if (args == null) {
paramTypes = new Class[0];
} else {
paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
paramTypes[i] = Object.class;
} else {
paramTypes[i] = args[i].getClass();
}
}
}
Method theMethod = getMethod(receiverClass, selector, paramTypes);
theMethod.setAccessible(true);
res = theMethod.invoke(target, args);
return new MethExecutorResult(res);
} catch (InvocationTargetException invTargEx) {
Throwable targEx = invTargEx.getTargetException();
if (targEx == null) {
return new MethExecutorResult(res);
} else {
return new MethExecutorResult(targEx);
}
}
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable t) {
return new MethExecutorResult(t);
}
}
/**
*
* Send the message "selector" to an instance of the class named "receiver". Return the result,
* including stack trace (if any).
*
*/
public static MethExecutorResult executeInstance(String receiver, String selector) {
try {
// get the class
Class receiverClass = Class.forName(receiver);
Object target = receiverClass.newInstance();
// invoke the method
Object res = null;
try {
Method theMethod = getMethod(receiverClass, selector, new Class[0]);
res = theMethod.invoke(target, new Object[0]);
return new MethExecutorResult(res);
} catch (InvocationTargetException invTargEx) {
Throwable targEx = invTargEx.getTargetException();
if (targEx == null) {
return new MethExecutorResult(res);
} else {
return new MethExecutorResult(targEx);
}
}
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable t) {
return new MethExecutorResult(t);
}
}
/**
*
* Send the message "selector" to an instance of the class named "receiver". Return the result,
* including stack trace (if any).
*
*/
public static MethExecutorResult executeInstance(String receiver, String selector, Class[] types,
Object[] args) {
try {
// get the class
Class receiverClass = Class.forName(receiver);
Constructor init = receiverClass.getDeclaredConstructor(new Class[0]);
init.setAccessible(true);
Object target = init.newInstance(new Object[0]);
// invoke the method
Object res = null;
try {
Method theMethod = getMethod(receiverClass, selector, types);
res = theMethod.invoke(target, args);
return new MethExecutorResult(res);
} catch (InvocationTargetException invTargEx) {
Throwable targEx = invTargEx.getTargetException();
if (targEx == null) {
return new MethExecutorResult(res);
} else {
return new MethExecutorResult(targEx);
}
}
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable t) {
return new MethExecutorResult(t);
}
}
/**
*
* A small program for testing this class.
*
*/
public static String testMethod1() {
return "The result is: " + System.currentTimeMillis();
}
public static String testMethod2() {
throw new ArrayIndexOutOfBoundsException("frip");
}
public static void main(String[] args) {
MethExecutorResult result = null;
result = MethExecutor.execute("hydra.MethExecutor", "testMethod1");
System.out.println(result.toString());
result = MethExecutor.execute("hydra.MethExecutor", "testMethod2");
System.out.println(result.toString());
}
}