All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kitesdk.compat.DynMethods Maven / Gradle / Ivy

Go to download

The Kite Hadoop Compatibility module provides dynamic bindings to use multiple versions of hadoop APIs.

There is a newer version: 1.1.0
Show newest version
/*
 * Copyright 2013 Cloudera Inc.
 *
 * Licensed 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.kitesdk.compat;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;

public class DynMethods {

  /**
   * Convenience wrapper class around {@link java.lang.reflect.Method}.
   *
   * Allows callers to invoke the wrapped method with all Exceptions wrapped by
   * RuntimeException, or with a single Exception catch block.
   */
  public static class UnboundMethod {

    private final Method method;
    private final String name;
    private final int argLength;

    UnboundMethod(Method method, String name) {
      this.method = method;
      this.name = name;
      this.argLength = (method == null || method.isVarArgs()) ? -1 :
          method.getParameterTypes().length;
    }

    @SuppressWarnings("unchecked")
    public  R invokeChecked(Object target, Object... args) throws Exception {
      try {
        if (argLength < 0) {
          return (R) method.invoke(target, args);
        } else {
          return (R) method.invoke(target, Arrays.copyOfRange(args, 0, argLength));
        }

      } catch (InvocationTargetException e) {
        // rethrow the cause is an exception
        Throwables.propagateIfPossible(e.getCause(), Exception.class);
        // otherwise, propagate the throwable
        throw Throwables.propagate(e.getCause());
      }
    }

    public  R invoke(Object target, Object... args) {
      try {
        return this.invokeChecked(target, args);
      } catch (Exception e) {
        throw Throwables.propagate(e);
      }
    }

    /**
     * Returns this method as a BoundMethod for the given receiver.
     *
     * @param receiver an Object to receive the method invocation
     * @return a {@link BoundMethod} for this method and the receiver
     * @throws IllegalStateException if the method is static
     * @throws IllegalArgumentException if the receiver's class is incompatible
     */
    public BoundMethod bind(Object receiver) {
      Preconditions.checkState(!isStatic(),
          "Cannot bind static method " + method.toGenericString());
      Preconditions.checkArgument(
          method.getDeclaringClass().isAssignableFrom(receiver.getClass()),
          "Cannot bind " + method.toGenericString() + " to instance of " +
              receiver.getClass());

      return new BoundMethod(this, receiver);
    }

    /**
     * @return whether the method is a static method
     */
    public boolean isStatic() {
      return Modifier.isStatic(method.getModifiers());
    }

    /**
     * Returns this method as a StaticMethod.
     *
     * @return a {@link StaticMethod} for this method
     * @throws IllegalStateException if the method is not static
     */
    public StaticMethod asStatic() {
      Preconditions.checkState(isStatic(), "Method is not static");
      return new StaticMethod(this);
    }

    public String toString() {
      return Objects.toStringHelper(this)
          .add("name", name)
          .add("method", method.toGenericString())
          .toString();
    }

    /**
     * Singleton {@link UnboundMethod}, performs no operation and returns null.
     */
    private static UnboundMethod NOOP = new UnboundMethod(null, "NOOP") {
      @Override
      public  R invokeChecked(Object target, Object... args) throws Exception {
        return null;
      }

      @Override
      public BoundMethod bind(Object receiver) {
        return new BoundMethod(this, receiver);
      }

      @Override
      public StaticMethod asStatic() {
        return new StaticMethod(this);
      }

      @Override
      public boolean isStatic() {
        return true;
      }

      @Override
      public String toString() {
        return Objects.toStringHelper(this)
            .add("name", "noop")
            .toString();
      }
    };
  }

  public static class BoundMethod {
    private final UnboundMethod method;
    private final Object receiver;

    private BoundMethod(UnboundMethod method, Object receiver) {
      this.method = method;
      this.receiver = receiver;
    }

    public  R invokeChecked(Object... args) throws Exception {
      return method.invokeChecked(receiver, args);
    }

    public  R invoke(Object... args) {
      return method.invoke(receiver, args);
    }
  }

  public static class StaticMethod {
    private final UnboundMethod method;

    private StaticMethod(UnboundMethod method) {
      this.method = method;
    }

    public  R invokeChecked(Object... args) throws Exception {
      return method.invokeChecked(null, args);
    }

    public  R invoke(Object... args) {
      return method.invoke(null, args);
    }
  }

  public static class Builder {
    private final String name;
    private ClassLoader loader = Thread.currentThread().getContextClassLoader();
    private UnboundMethod method = null;

    public Builder(String methodName) {
      this.name = methodName;
    }

    /**
     * Set the {@link ClassLoader} used to lookup classes by name.
     * 

* If not set, the current thread's ClassLoader is used. * * @param loader a ClassLoader * @return this Builder for method chaining */ public Builder loader(ClassLoader loader) { this.loader = loader; return this; } /** * If no implementation has been found, adds a NOOP method. * * Note: calls to impl will not match after this method is called! * * @return this Builder for method chaining */ public Builder defaultNoop() { if (method == null) { this.method = UnboundMethod.NOOP; } return this; } /** * Checks for an implementation, first finding the given class by name. * * @param className name of a class * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder impl(String className, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Class targetClass = Class.forName(className, true, loader); impl(targetClass, methodName, argClasses); } catch (ClassNotFoundException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * The name passed to the constructor is the method name used. * * @param className name of a class * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder impl(String className, Class... argClasses) { impl(className, name, argClasses); return this; } /** * Checks for a method implementation. * * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder impl(Class targetClass, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new UnboundMethod( targetClass.getMethod(methodName, argClasses), name); } catch (NoSuchMethodException e) { // not the right implementation } return this; } /** * Checks for a method implementation. * * The name passed to the constructor is the method name used. * * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder impl(Class targetClass, Class... argClasses) { impl(targetClass, name, argClasses); return this; } public Builder ctorImpl(Class targetClass, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new DynConstructors.Builder() .impl(targetClass, argClasses) .buildChecked(); } catch (NoSuchMethodException e) { // not the right implementation } return this; } public Builder ctorImpl(String className, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { this.method = new DynConstructors.Builder() .impl(className, argClasses) .buildChecked(); } catch (NoSuchMethodException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * @param className name of a class * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder hiddenImpl(String className, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Class targetClass = Class.forName(className, true, loader); hiddenImpl(targetClass, methodName, argClasses); } catch (ClassNotFoundException e) { // not the right implementation } return this; } /** * Checks for an implementation, first finding the given class by name. * * The name passed to the constructor is the method name used. * * @param className name of a class * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder hiddenImpl(String className, Class... argClasses) { hiddenImpl(className, name, argClasses); return this; } /** * Checks for a method implementation. * * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder hiddenImpl(Class targetClass, String methodName, Class... argClasses) { // don't do any work if an implementation has been found if (method != null) { return this; } try { Method hidden = targetClass.getDeclaredMethod(methodName, argClasses); AccessController.doPrivileged(new MakeAccessible(hidden)); this.method = new UnboundMethod(hidden, name); } catch (SecurityException e) { // unusable } catch (NoSuchMethodException e) { // not the right implementation } return this; } /** * Checks for a method implementation. * * The name passed to the constructor is the method name used. * * @param argClasses argument classes for the method * @return this Builder for method chaining * @see {@link java.lang.Class#forName(String)} * @see {@link java.lang.Class#getMethod(String, Class[])} */ public Builder hiddenImpl(Class targetClass, Class... argClasses) { hiddenImpl(targetClass, name, argClasses); return this; } /** * Returns the first valid implementation as a UnboundMethod or throws a * NoSuchMethodException if there is none. * * @return a {@link UnboundMethod} with a valid implementation * @throws NoSuchMethodException if no implementation was found */ public UnboundMethod buildChecked() throws NoSuchMethodException { if (method != null) { return method; } else { throw new NoSuchMethodException("Cannot find method: " + name); } } /** * Returns the first valid implementation as a UnboundMethod or throws a * RuntimeError if there is none. * * @return a {@link UnboundMethod} with a valid implementation * @throws RuntimeException if no implementation was found */ public UnboundMethod build() { if (method != null) { return method; } else { throw new RuntimeException("Cannot find method: " + name); } } /** * Returns the first valid implementation as a BoundMethod or throws a * NoSuchMethodException if there is none. * * @param receiver an Object to receive the method invocation * @return a {@link BoundMethod} with a valid implementation and receiver * @throws IllegalStateException if the method is static * @throws IllegalArgumentException if the receiver's class is incompatible * @throws NoSuchMethodException if no implementation was found */ public BoundMethod buildChecked(Object receiver) throws NoSuchMethodException { return buildChecked().bind(receiver); } /** * Returns the first valid implementation as a BoundMethod or throws a * RuntimeError if there is none. * * @param receiver an Object to receive the method invocation * @return a {@link BoundMethod} with a valid implementation and receiver * @throws IllegalStateException if the method is static * @throws IllegalArgumentException if the receiver's class is incompatible * @throws RuntimeException if no implementation was found */ public BoundMethod build(Object receiver) { return build().bind(receiver); } /** * Returns the first valid implementation as a StaticMethod or throws a * NoSuchMethodException if there is none. * * @return a {@link StaticMethod} with a valid implementation * @throws IllegalStateException if the method is not static * @throws NoSuchMethodException if no implementation was found */ public StaticMethod buildStaticChecked() throws NoSuchMethodException { return buildChecked().asStatic(); } /** * Returns the first valid implementation as a StaticMethod or throws a * RuntimeException if there is none. * * @return a {@link StaticMethod} with a valid implementation * @throws IllegalStateException if the method is not static * @throws RuntimeException if no implementation was found */ public StaticMethod buildStatic() { return build().asStatic(); } } private static class MakeAccessible implements PrivilegedAction { private Method hidden; public MakeAccessible(Method hidden) { this.hidden = hidden; } @Override public Void run() { hidden.setAccessible(true); return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy