package org.radargun.config; import java.lang.annotation.Annotation; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner; import io.github.lukehutch.fastclasspathscanner.scanner.ClassInfo; import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; /** * Helper for listing classes on classpath. */ public final class ClasspathScanner { private static final Log log = LogFactory.getLog(ClasspathScanner.class); private ClasspathScanner() { } @SuppressWarnings("unchecked") /** * * Scan the classpath for classes with the specified annotations * * @param superClass * restrict the search to annotations that are subclasses of this class, or * <code>null</code> to search all classes * @param annotationClass * the annotation to find. Required. * @param requirePackage * restrict the search for the annotation to this package, or <code>null</code> to * search all packages * @param consumer * a Consumer to process the matching annotion classes */ public static <TClass, TAnnotation extends Annotation> void scanClasspath(Class<TClass> superClass, Class<TAnnotation> annotationClass, String requirePackage, Consumer<Class<? extends TClass>> consumer) { if (annotationClass == null) { throw new IllegalArgumentException("An annotation class must be specified"); } FastClasspathScanner fcs; if (requirePackage != null) { fcs = new FastClasspathScanner("!", requirePackage); } else { fcs = new FastClasspathScanner("!"); } fcs.registerClassLoaderHandler(new AntClassLoaderHandler()); ScanResult scanResults = fcs.scan(); List<String> matches; if (superClass != null) { matches = scanResults.getClassNameToClassInfo().values().stream() .filter(ci -> ci.hasSuperclass(superClass.getName()) || ci.implementsInterface(superClass.getName())) .filter(ci -> ci.hasAnnotation(annotationClass.getName())).map(ClassInfo::getClassName) .collect(Collectors.toList()); log.debug("Found " + matches.size() + " classes with annotation '" + annotationClass.getName() + "' and super class '" + superClass.getName() + "'"); } else { matches = scanResults.getClassNameToClassInfo().values().stream() .filter(ci -> ci.hasAnnotation(annotationClass.getName())).map(ClassInfo::getClassName) .collect(Collectors.toList()); log.debug("Found " + matches.size() + " classes with annotation '" + annotationClass.getName() + "'"); } // Only load matched classes to avoid any spourious exceptions thrown from static blocks for (String className : matches) { Class<?> clazz; try { clazz = Class.forName(className); consumer.accept((Class<? extends TClass>) clazz); } catch (Throwable e) { // static ctor can throw non-wrapped error log.error("Cannot load class " + className, e); continue; } } } }