private static Method getMethod(Class targetClass, String methodName, Class[] parameterTypes)
      throws NoSuchMethodException {
    List<Method> matchingMethods = new ArrayList<>();
    for (Class theClass = targetClass; theClass != null; theClass = theClass.getSuperclass()) {
      Method[] methods = theClass.getDeclaredMethods();

      NEXT_METHOD:

      for (Method method : methods) {
        if (!method.getName().equals(methodName)) {
          continue;
        }

        Class[] methodParameterTypes = method.getParameterTypes();
        if (methodParameterTypes.length != parameterTypes.length) {
          continue;
        }

        for (int j = 0; j < methodParameterTypes.length; j++) {
          if (parameterTypes[j] == null) {
            if (methodParameterTypes[j].isPrimitive()) {
              // this parameter is not ok, the parameter is a primitive 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 (!methodParameterTypes[j].isAssignableFrom(parameterTypes[j])) {
            Class methodParameterType = methodParameterTypes[j];
            Class parameterType = parameterTypes[j];

            if (methodParameterType.isPrimitive()) {
              if (methodParameterType.equals(boolean.class) && parameterType.equals(Boolean.class)
                  || methodParameterType.equals(short.class) && parameterType.equals(Short.class)
                  || methodParameterType.equals(int.class) && parameterType.equals(Integer.class)
                  || methodParameterType.equals(long.class) && parameterType.equals(Long.class)
                  || methodParameterType.equals(float.class) && parameterType.equals(Float.class)
                  || methodParameterType.equals(double.class) && parameterType.equals(Double.class)
                  || methodParameterType.equals(char.class) && parameterType.equals(Character.class)
                  || methodParameterType.equals(byte.class) && parameterType.equals(Byte.class)) {

                // This parameter is okay, try the next one
                continue;
              }
            }
            continue NEXT_METHOD;
          }
        }

        matchingMethods.add(method);
      }

      // 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()) {
      StringBuilder sb = new StringBuilder();
      sb.append("Could not find method ");
      sb.append(methodName);
      sb.append(" with ");
      sb.append(parameterTypes.length);
      sb.append(" parameters [");
      for (int i = 0; i < parameterTypes.length; i++) {
        String name = parameterTypes[i] == null ? null : parameterTypes[i].getName();
        sb.append(name);
        if (i < parameterTypes.length - 1) {
          sb.append(", ");
        }
      }
      sb.append("] in class ");
      sb.append(targetClass.getName());
      throw new NoSuchMethodException(sb.toString());
    }

    if (matchingMethods.size() > 1) {
      StringBuilder sb = new StringBuilder();
      sb.append("Method is ambiguous ");
      sb.append(methodName);
      sb.append(" with ");
      sb.append(parameterTypes.length);
      sb.append(" parameters [");
      for (int i = 0; i < parameterTypes.length; i++) {
        String name = parameterTypes[i] == null ? null : parameterTypes[i].getName();
        sb.append(name);
        if (i < parameterTypes.length - 1) {
          sb.append(", ");
        }
      }
      sb.append("] in class ");
      sb.append(targetClass.getName());
      sb.append(" methods=").append(matchingMethods);
      throw new NoSuchMethodException(sb.toString());

    } else {
      return matchingMethods.get(0);
    }
  }
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
geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/MethodInvoker.java