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

org.apache.ratis.util.ReflectionUtils Maven / Gradle / Ivy

There is a newer version: 3.1.2
Show 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.ratis.util;


import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Reflection related utility methods.
 */
public interface ReflectionUtils {
  final class Constructors {
    private Constructors() {}

    /**
     * Cache of constructors for each class. Pins the classes so they
     * can't be garbage collected until ReflectionUtils can be collected.
     */
    private static final Map>, Constructor> CONSTRUCTORS
        = new ConcurrentHashMap<>();

    private static  Constructor get(Class clazz, Class[] argClasses)
        throws NoSuchMethodException {
      Objects.requireNonNull(clazz, "clazz == null");

      final List> key = new ArrayList<>(argClasses.length + 1);
      key.add(clazz);
      key.addAll(Arrays.asList(argClasses));

      @SuppressWarnings("unchecked")
      Constructor ctor = (Constructor) CONSTRUCTORS.get(key);
      if (ctor == null) {
        ctor = clazz.getDeclaredConstructor(argClasses);
        ctor.setAccessible(true);
        CONSTRUCTORS.put(key, ctor);
      }
      return ctor;
    }
  }

  final class Classes {
    private static final Class[] EMPTY_ARRAY = {};

    private static final Map>>>
        CLASSES = new WeakHashMap<>();

    /** Sentinel value to store negative cache results in {@link #CLASSES}. */
    private static final Class NEGATIVE_CACHE_SENTINEL = NegativeCacheSentinel.class;

    /**
     * A unique class which is used as a sentinel value in the caching
     * for getClassByName. {@link #getClassByNameOrNull(String)}
     */
    private static final class NegativeCacheSentinel {}

    private static final ClassLoader CLASS_LOADER = Optional.ofNullable(
            Thread.currentThread().getContextClassLoader()).orElseGet(ReflectionUtils.class::getClassLoader);

    private static Map>> getClassMap() {
      Map>> map;
      synchronized (CLASSES) {
        map = CLASSES.get(CLASS_LOADER);
        if (map == null) {
          map = Collections.synchronizedMap(new WeakHashMap<>());
          CLASSES.put(CLASS_LOADER, map);
        }
      }
      return map;
    }
  }

  static ClassLoader getClassLoader() {
    return Classes.CLASS_LOADER;
  }

  /**
   * Load a class by name, returning null rather than throwing an exception
   * if it couldn't be loaded. This is to avoid the overhead of creating
   * an exception.
   *
   * @param name the class name
   * @return the class object, or null if it could not be found.
   */
  static Class getClassByNameOrNull(String name) {
    return getClassByNameOrNull(name, null);
  }

  /**
   * Load a class by name through a specific classloader, returning null rather
   * than throwing an exception if it couldn't be loaded. This is to avoid the
   * overhead of creating an exception.
   *
   * @param name the class name
   * @param classLoader the classloader
   * @return the class object, or null if it could not be found.
   */
  static Class getClassByNameOrNull(String name, ClassLoader classLoader) {
    final Map>> map = Classes.getClassMap();

    Class clazz = null;
    WeakReference> ref = map.get(name);
    if (ref != null) {
      clazz = ref.get();
    }

    if (clazz == null) {
      try {
        clazz = Class.forName(name, true,
            classLoader != null ? classLoader : Classes.CLASS_LOADER);
      } catch (ClassNotFoundException e) {
        // Leave a marker that the class isn't found
        map.put(name, new WeakReference<>(Classes.NEGATIVE_CACHE_SENTINEL));
        return null;
      }
      // two putters can race here, but they'll put the same class
      map.put(name, new WeakReference<>(clazz));
      return clazz;
    } else if (clazz == Classes.NEGATIVE_CACHE_SENTINEL) {
      return null; // not found
    } else {
      // cache hit
      return clazz;
    }
  }

  /**
   * Load a class by name.
   *
   * @param name the class name.
   * @return the class object.
   * @throws ClassNotFoundException if the class is not found.
   */
  static Class getClassByName(String name) throws ClassNotFoundException {
    return getClassByName(name, null);
  }

  /**
   * Load a class by name through a specific classloader.
   *
   * @param name the class name.
   * @param classLoader the classloader.
   * @return the class object.
   * @throws ClassNotFoundException if the class is not found.
   */
  static Class getClassByName(String name, ClassLoader classLoader)
      throws ClassNotFoundException {
    Class ret = getClassByNameOrNull(name, classLoader);
    if (ret == null) {
      throw new ClassNotFoundException("Class " + name + " not found");
    }
    return ret;
  }

  /**
   * Create an object for the given class using its default constructor.
   */
  static  T newInstance(Class clazz) {
    return newInstance(clazz, Classes.EMPTY_ARRAY);
  }

  /**
   * Create an object for the given class using the specified constructor.
   *
   * @param clazz class of which an object is created
   * @param argClasses argument classes of the constructor
   * @param args actual arguments to be passed to the constructor
   * @param  class type of clazz
   * @return a new object
   */
  static  T newInstance(Class clazz, Class[] argClasses, Object... args) {
    final Constructor ctor;
    try {
      ctor = Constructors.get(clazz, argClasses);
    } catch (NoSuchMethodException e) {
      throw new UnsupportedOperationException(
          "Unable to find suitable constructor for class " + clazz.getName()
          + ", argument classes = " + Arrays.toString(argClasses), e);
    }
    return instantiate(clazz.getName(), ctor, args);
  }

  static  T instantiate(final String className, Constructor ctor, Object[] ctorArgs) {
    try {
      ctor.setAccessible(true);
      return ctor.newInstance(ctorArgs);
    } catch (IllegalAccessException e) {
      throw new UnsupportedOperationException(
          "Unable to access specified class " + className, e);
    } catch (InstantiationException e) {
      throw new UnsupportedOperationException(
          "Unable to instantiate specified class " + className, e);
    } catch (InvocationTargetException e) {
      throw new UnsupportedOperationException(
          "Constructor threw an exception for " + className, e);
    }
  }

  /** Is the given object an instance of one of the given classes? */
  static boolean isInstance(Object obj, Class... classes) {
    for(Class c : classes) {
      if (c.isInstance(obj)) {
        return true;
      }
    }
    return false;
  }

  static String getImplClassName(Class clazz) {
    return clazz.getPackage().getName() + ".impl." + JavaUtils.getClassSimpleName(clazz) + "Impl";
  }

  static  Class getImplClass(Class base) {
    return getClass(getImplClassName(base), base);
  }

  static  Class getClass(String subClassName, Class base) {
    try {
      return getClassByName(subClassName, base.getClassLoader()).asSubclass(base);
    } catch (ClassNotFoundException e) {
      throw new IllegalArgumentException("Failed to get class "
          + subClassName + " as a subclass of " + base, e);
    }
  }

  static  T instantiateException(Class clazz) throws Exception {
    final Constructor c = clazz.getConstructor();
    c.setAccessible(true);
    return c.newInstance();
  }

  static  T instantiateException(Class clazz, String message) throws Exception {
    if (message == null) {
      return instantiateException(clazz);
    }
    final Constructor c = clazz.getConstructor(String.class);
    c.setAccessible(true);
    return c.newInstance(message);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy