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

org.apache.hadoop.util.dynamic.DynMethods Maven / Gradle / Ivy

The newest version!
/*
 * 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.hadoop.util.dynamic;

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.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.util.Preconditions;

import static org.apache.hadoop.util.Preconditions.checkState;


/**
 * Dynamic method invocation.
 * Taken from {@code org.apache.parquet.util.DynMethods}.
 */
@InterfaceAudience.LimitedPrivate("testing")
@InterfaceStability.Unstable
public final class DynMethods {

  private static final Logger LOG = LoggerFactory.getLogger(DynMethods.class);

  private DynMethods() {
  }

  /**
   * Convenience wrapper class around {@link 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 { if (argLength != args.length) { LOG.error("expected {} arguments but got {}", argLength, args.length); } return (R) method.invoke(target, Arrays.copyOfRange(args, 0, argLength)); } } catch (InvocationTargetException e) { throwIfInstance(e.getCause(), Exception.class); throwIfInstance(e.getCause(), RuntimeException.class); throw new RuntimeException(e.getCause()); } } public R invoke(Object target, Object... args) { try { return this.invokeChecked(target, args); } catch (Exception e) { throwIfInstance(e, RuntimeException.class); throw new RuntimeException(e); } } /** * Invoke a static method. * @param args arguments. * @return result. * @param type of result. */ public R invokeStatic(Object... args) { checkState(isStatic(), "Method is not static %s", toString()); return invoke(null, args); } /** * 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) { checkState(!isStatic(), "Cannot bind static method %s", method.toGenericString()); Preconditions.checkArgument(method.getDeclaringClass().isAssignableFrom(receiver.getClass()), "Cannot bind %s to instance of %s", method.toGenericString(), receiver.getClass()); return new BoundMethod(this, receiver); } /** * @return whether the method is a static method */ public boolean isStatic() { return Modifier.isStatic(method.getModifiers()); } /** * @return whether the method is a noop */ public boolean isNoop() { return this == NOOP; } /** * Returns this method as a StaticMethod. * @return a {@link StaticMethod} for this method * @throws IllegalStateException if the method is not static */ public StaticMethod asStatic() { checkState(isStatic(), "Method is not static"); return new StaticMethod(this); } public String toString() { return "DynMethods.UnboundMethod(name=" + name + " method=" + method.toGenericString() + ")"; } /** * Singleton {@link UnboundMethod}, performs no operation and returns null. */ private static final 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 "DynMethods.UnboundMethod(NOOP)"; } }; } public static final 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 final 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); } } /** * If the given throwable is an instance of E, throw it as an E. * @param t an exception instance * @param excClass an exception class t may be an instance of * @param the type of exception that will be thrown if throwable is an instance * @throws E if t is an instance of E */ @SuppressWarnings("unchecked") public static void throwIfInstance(Throwable t, Class excClass) throws E { if (excClass.isAssignableFrom(t.getClass())) { // the throwable is already an exception, so throw it throw (E)t; } } public static final 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 classLoader a ClassLoader * @return this Builder for method chaining */ public Builder loader(ClassLoader classLoader) { this.loader = classLoader; 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 orNoop() { 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 */ 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) { // class not found on supplied classloader. LOG.debug("failed to load class {}", className, e); } 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 */ public Builder impl(String className, Class... argClasses) { impl(className, name, argClasses); return this; } /** * Checks for a method implementation. * @param targetClass the class to check for an implementation * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ 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 LOG.debug("failed to load method {} from class {}", methodName, targetClass, e); } return this; } /** * Checks for a method implementation. *

* The name passed to the constructor is the method name used. * @param targetClass the class to check for an implementation * @param argClasses argument classes for the method * @return this Builder for method chaining */ 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 LOG.debug("failed to load constructor arity {} from class {}", argClasses.length, targetClass, e); } 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 LOG.debug("failed to load constructor arity {} from class {}", argClasses.length, className, e); } 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 */ 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) { // class not found on supplied classloader. LOG.debug("failed to load class {}", className, e); } 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 */ public Builder hiddenImpl(String className, Class... argClasses) { hiddenImpl(className, name, argClasses); return this; } /** * Checks for a method implementation. * @param targetClass the class to check for an implementation * @param methodName name of a method (different from constructor) * @param argClasses argument classes for the method * @return this Builder for method chaining */ 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 | NoSuchMethodException e) { // unusable or not the right implementation LOG.debug("failed to load method {} from class {}", methodName, targetClass, e); } return this; } /** * Checks for a method implementation. *

* The name passed to the constructor is the method name used. * @param targetClass the class to check for an implementation * @param argClasses argument classes for the method * @return this Builder for method chaining */ 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 final class MakeAccessible implements PrivilegedAction { private Method hidden; MakeAccessible(Method hidden) { this.hidden = hidden; } @Override public Void run() { hidden.setAccessible(true); return null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy