This is an automated email from the ASF dual-hosted git repository. sai_boorlagadda pushed a commit to branch feature/GEODE-5787-dunit-internal in repository https://gitbox.apache.org/repos/asf/geode.git
commit 27548dcf3b2a080670120128d0150f12dc651b1e Author: Sai Boorlagadda <sboorlaga...@pivotal.io> AuthorDate: Tue Oct 2 11:44:28 2018 -0700 GEODE-5787: move MethExecutor into dunit --- .../main/java/org/apache/geode/test/dunit/VM.java | 3 +- .../geode/test/dunit/standalone/DUnitLauncher.java | 2 +- .../geode/test/dunit/standalone/MethExecutor.java | 363 +++++++++++++++++++++ .../test/dunit/standalone/MethExecutorResult.java | 174 ++++++++++ .../geode/test/dunit/standalone/RemoteDUnitVM.java | 2 - .../test/dunit/standalone/RemoteDUnitVMIF.java | 2 - 6 files changed, 539 insertions(+), 7 deletions(-) diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java index 32643ec..74f618a 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java @@ -24,10 +24,9 @@ import java.rmi.RemoteException; import java.util.List; import java.util.concurrent.Callable; -import hydra.MethExecutorResult; - import org.apache.geode.internal.process.ProcessUtils; import org.apache.geode.test.dunit.standalone.BounceResult; +import org.apache.geode.test.dunit.standalone.MethExecutorResult; import org.apache.geode.test.dunit.standalone.RemoteDUnitVMIF; import org.apache.geode.test.dunit.standalone.StandAloneDUnitEnv; import org.apache.geode.test.dunit.standalone.VersionManager; diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/DUnitLauncher.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/DUnitLauncher.java index 8f82f9b..d427896 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/DUnitLauncher.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/DUnitLauncher.java @@ -48,7 +48,7 @@ import java.util.Properties; import batterytest.greplogs.ExpectedStrings; import batterytest.greplogs.LogConsumer; -import hydra.MethExecutorResult; +import org.apache.geode.test.dunit.standalone.MethExecutorResult; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutor.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutor.java new file mode 100644 index 0000000..7913739 --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutor.java @@ -0,0 +1,363 @@ +/* + * 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.geode.test.dunit.standalone; + +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()); + } +} diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutorResult.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutorResult.java new file mode 100644 index 0000000..b1a2061 --- /dev/null +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/MethExecutorResult.java @@ -0,0 +1,174 @@ +/* + * 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.geode.test.dunit.standalone; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; + +/** + * + * The result of a MethExecutor execute method. + * + */ +public class MethExecutorResult implements Serializable { + + /** + * A "result" object that indicates that an exception occurred while invoking the method + */ + public static final Serializable EXCEPTION_OCCURRED = new Serializable() { + public boolean equals(Object o) { + // Allows instances to be compared across VMs + return o != null && this.getClass().equals(o.getClass()); + } + + public String toString() { + return "EXCEPTION_OCCURRED"; + } + }; + + /** + * A "exception" object that indicates that an exception could not be serialized. + */ + public static final Throwable NONSERIALIZABLE_EXCEPTION = new Throwable() { + public boolean equals(Object o) { + // Allows instances to be compared across VMs + return o != null && this.getClass().equals(o.getClass()); + } + + public String toString() { + return "NONSERIALIZABLE_EXCEPTION"; + } + }; + + + //////////////////// Instance Methods /////////////////////////// + + /** The result of execution (may be an exception or error type) */ + private Object result; + + /** The exception that resulted from invoking the method */ + private Throwable exception; + + /** Type of the exception (if applicable) */ + private String exceptionClassName; + + /** Message of the exception (if applicable) */ + private String exceptionMessage; + + /** Stack trace information (if applicable) */ + private String stackTrace; + + public MethExecutorResult() { + this.result = null; + } + + public MethExecutorResult(Object result) { + this.result = result; + } + + /** + * This constructor is invoked when invoking a method resulted in an exception being thrown. The + * "result" is set to {@link #EXCEPTION_OCCURRED}. If the exception could not be serialized, + * {@link #getException()} will return IOException with the exception stack as the message. + */ + public MethExecutorResult(Throwable thr) { + this.result = EXCEPTION_OCCURRED; + this.exceptionClassName = thr.getClass().getName(); + this.exceptionMessage = thr.getMessage(); + + StringWriter sw = new StringWriter(); + + thr.printStackTrace(new PrintWriter(sw, true)); + this.stackTrace = sw.toString(); + + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(thr); + this.exception = thr; + + } catch (IOException ex) { + sw = new StringWriter(); + ex.printStackTrace(new PrintWriter(sw, true)); + this.exception = new IOException(sw.toString()); + } + } + + public String toString() { + StringBuffer s = new StringBuffer(); + s.append(this.getResult()); + s.append("\n"); + if (this.getStackTrace() != null) { + s.append(this.getStackTrace()); + } + return s.toString(); + } + + /** + * Returns the result of the method call. If an exception was thrown during the method call, + * {@link #EXCEPTION_OCCURRED} is returned. + * + * @see #exceptionOccurred() + */ + public Object getResult() { + return this.result; + } + + /** + * Returns the name of the exception class of the exception that was thrown while invoking a + * method. If no exception was thrown, <code>null</code> is returned. + */ + public String getExceptionClassName() { + return this.exceptionClassName; + } + + /** + * Returns the message of the exception that was thrown while invoking a method. If no exception + * was thrown, <code>null</code> is returned. + */ + public String getExceptionMessage() { + return this.exceptionMessage; + } + + /** + * Returns the stack trace of the exception that was thrown while invoking a method. If no + * exception was thrown, <code>null</code> is returned. + */ + public String getStackTrace() { + return this.stackTrace; + } + + /** + * Returns the exception that was thrown while invoking a method. If the exception could not be + * serialized, then {@link #NONSERIALIZABLE_EXCEPTION} is returned. If no exception was thrown, + * <code>null</code> is returned. + */ + public Throwable getException() { + return this.exception; + } + + /** + * Returns whether or not an exception occurred while invoking the method + */ + public boolean exceptionOccurred() { + return EXCEPTION_OCCURRED.equals(this.result); + } + +} diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVM.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVM.java index d02ce74..382ea04 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVM.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVM.java @@ -18,8 +18,6 @@ import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.concurrent.TimeUnit; -import hydra.MethExecutor; -import hydra.MethExecutorResult; import org.apache.logging.log4j.Logger; import org.apache.geode.internal.logging.LogService; diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVMIF.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVMIF.java index 9db6b06..b8400e7 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVMIF.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/standalone/RemoteDUnitVMIF.java @@ -17,8 +17,6 @@ package org.apache.geode.test.dunit.standalone; import java.rmi.Remote; import java.rmi.RemoteException; -import hydra.MethExecutorResult; - public interface RemoteDUnitVMIF extends Remote { MethExecutorResult executeMethodOnObject(Object target, String methodName) throws RemoteException;