Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
Expand All @@ -37,22 +36,11 @@
*
* @author Pascal Schumacher (https://github.com/PascalSchumacher)
*/
abstract class ClassGraphFacade {
class ClassGraphFacade {

private static final ConcurrentHashMap<Class<?>, List<Class<?>>> typeToConcreteSubTypes = new ConcurrentHashMap<>();
private static final ScanResult scanResult = new ClassGraph().enableSystemJarsAndModules().enableClassInfo().scan();
private final static ScanResult scanResult = new ClassGraph().enableSystemJarsAndModules().enableClassInfo().scan();

/**
* Searches the classpath for all public concrete subtypes of the given interface or abstract class.
*
* @param type to search concrete subtypes of
* @return a list of all concrete subtypes found
*/
public static <T> List<Class<?>> getPublicConcreteSubTypesOf(final Class<T> type) {
return typeToConcreteSubTypes.computeIfAbsent(type, ClassGraphFacade::searchForPublicConcreteSubTypesOf);
}

private static <T> List<Class<?>> searchForPublicConcreteSubTypesOf(final Class<T> type) {
static List<Class<?>> searchForPublicConcreteSubTypesOf(final Class<?> type) {
String typeName = type.getName();
ClassInfoList subTypes = type.isInterface() ? scanResult.getClassesImplementing(typeName) : scanResult.getSubclasses(typeName);
List<Class<?>> loadedSubTypes = subTypes.filter(subType -> subType.isPublic() && !subType.isAbstract()).loadClasses(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

Expand All @@ -52,6 +53,11 @@
*/
public final class ReflectionUtils {

private static final ConcurrentHashMap<Class<?>, List<Class<?>>> typeToConcreteSubTypes = new ConcurrentHashMap<>();

// NOTE: indirection to avoid loading ClassGraphFacade class too early
private static volatile Function<Class<?>, List<Class<?>>> typeToConcreteSubTypesProvider = ReflectionUtils::defaultTypeToConcreteSubTypesProvider;

private ReflectionUtils() {
}

Expand Down Expand Up @@ -402,7 +408,25 @@ public static boolean isTypeVariable(final Type type) {
* @return a list of all concrete subtypes found
*/
public static <T> List<Class<?>> getPublicConcreteSubTypesOf(final Class<T> type) {
return ClassGraphFacade.getPublicConcreteSubTypesOf(type);
return typeToConcreteSubTypes.computeIfAbsent(type, typeToConcreteSubTypesProvider);
}

/**
* Override the default implementation of ClassGraphFacade for searching the
* classpath for any concrete subtype of given interface or abstract class.
*
* @param typeToConcreteSubTypesProvider custom implementation
*/
public static void setPublicConcreteSubTypeProvider(
final Function<Class<?>, List<Class<?>>> typeToConcreteSubTypesProvider) {
if (typeToConcreteSubTypesProvider == null) {
throw new IllegalArgumentException();
}
ReflectionUtils.typeToConcreteSubTypesProvider = typeToConcreteSubTypesProvider;
}

private static List<Class<?>> defaultTypeToConcreteSubTypesProvider(final Class<?> cls) {
return ClassGraphFacade.searchForPublicConcreteSubTypesOf(cls);
}

/**
Expand Down