org.apache.ratis.util.ReflectionUtils Maven / Gradle / Ivy
/*
* *
* * 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 extends BASE> getImplClass(Class base) {
return getClass(getImplClassName(base), base);
}
static Class extends BASE> 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