Skip to content

Commit

Permalink
Make ReflectionUtils non-static (#756, #757)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukehutch committed Mar 4, 2023
1 parent 9a84cd9 commit 2bb3192
Show file tree
Hide file tree
Showing 45 changed files with 641 additions and 425 deletions.
5 changes: 4 additions & 1 deletion src/main/java/io/github/classgraph/AnnotationInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,13 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg
} else if (!annotationClass.isInstance(args[0])) {
return false;
}
final ReflectionUtils reflectionUtils = annotationInfo.scanResult == null
? new ReflectionUtils()
: annotationInfo.scanResult.reflectionUtils;
for (final Entry<String, Object> ent : annotationParameterValuesInstantiated.entrySet()) {
final String paramName = ent.getKey();
final Object paramVal = ent.getValue();
final Object otherParamVal = ReflectionUtils.invokeMethod(/* throwException = */ false,
final Object otherParamVal = reflectionUtils.invokeMethod(/* throwException = */ false,
args[0], paramName);
if ((paramVal == null) != (otherParamVal == null)) {
// Annotation values should never be null, but just to be safe
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/io/github/classgraph/ClassGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import nonapi.io.github.classgraph.classpath.SystemJarFinder;
import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService;
import nonapi.io.github.classgraph.concurrency.InterruptionChecker;
import nonapi.io.github.classgraph.reflection.ReflectionUtils;
import nonapi.io.github.classgraph.scanspec.AcceptReject;
import nonapi.io.github.classgraph.scanspec.ScanSpec;
import nonapi.io.github.classgraph.utils.JarUtils;
Expand Down Expand Up @@ -126,6 +127,8 @@ public enum CircumventEncapsulationMethod {
*/
public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE;

private final ReflectionUtils reflectionUtils;

/**
* If non-null, log while scanning.
*/
Expand All @@ -135,8 +138,9 @@ public enum CircumventEncapsulationMethod {

/** Construct a ClassGraph instance. */
public ClassGraph() {
reflectionUtils = new ReflectionUtils();
// Initialize ScanResult, if this is the first call to ClassGraph constructor
ScanResult.init();
ScanResult.init(reflectionUtils);
}

/**
Expand Down Expand Up @@ -1501,7 +1505,7 @@ public void run() {
try {
// Call scanner, but ignore the returned ScanResult
new Scanner(/* performScan = */ true, scanSpec, executorService, numParallelTasks,
scanResultProcessor, failureHandler, topLevelLog).call();
scanResultProcessor, failureHandler, reflectionUtils, topLevelLog).call();
} catch (final InterruptedException | CancellationException | ExecutionException e) {
// Call failure handler
failureHandler.onFailure(e);
Expand Down Expand Up @@ -1529,7 +1533,7 @@ private Future<ScanResult> scanAsync(final boolean performScan, final ExecutorSe
final int numParallelTasks) {
try {
return executorService.submit(new Scanner(performScan, scanSpec, executorService, numParallelTasks,
/* scanResultProcessor = */ null, /* failureHandler = */ null, topLevelLog));
/* scanResultProcessor = */ null, /* failureHandler = */ null, reflectionUtils, topLevelLog));
} catch (final InterruptedException e) {
// Interrupted during the Scanner constructor's execution (specifically, by getModuleOrder(),
// which is unlikely to ever actually be interrupted -- but this exception needs to be caught).
Expand Down Expand Up @@ -1764,7 +1768,7 @@ public List<ModuleRef> getModules() {
* @return The {@link ModulePathInfo}.
*/
public ModulePathInfo getModulePathInfo() {
scanSpec.modulePathInfo.getRuntimeInfo();
scanSpec.modulePathInfo.getRuntimeInfo(reflectionUtils);
return scanSpec.modulePathInfo;
}
}
4 changes: 3 additions & 1 deletion src/main/java/io/github/classgraph/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -2718,8 +2718,10 @@ public List<Object> getEnumConstantObjects() {
final Class<?> enumClass = loadClass();
final FieldInfoList consts = getEnumConstants();
final List<Object> constObjs = new ArrayList<>(consts.size());
final ReflectionUtils reflectionUtils = scanResult == null ? new ReflectionUtils()
: scanResult.reflectionUtils;
for (final FieldInfo constFieldInfo : consts) {
final Object constObj = ReflectionUtils.getStaticFieldVal(true, enumClass, constFieldInfo.getName());
final Object constObj = reflectionUtils.getStaticFieldVal(true, enumClass, constFieldInfo.getName());
if (constObj == null) {
throw new IllegalArgumentException("Could not read enum constant objects");
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/io/github/classgraph/ModulePathInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,22 @@ public class ModulePathInfo {
private final AtomicBoolean gotRuntimeInfo = new AtomicBoolean();

/** Fill in module info from VM commandline parameters. */
void getRuntimeInfo() {
void getRuntimeInfo(final ReflectionUtils reflectionUtils) {
// Only call this reflective method if ModulePathInfo is specifically requested, to avoid illegal
// access warning on some JREs, e.g. Adopt JDK 11 (#605)
if (!gotRuntimeInfo.getAndSet(true)) {
// Read the raw commandline arguments to get the module path override parameters.
// If the java.management module is not present in the deployed runtime (for JDK 9+), or the runtime
// does not contain the java.lang.management package (e.g. the Android build system, which also does
// not support JPMS currently), then skip trying to read the commandline arguments (#404).
final Class<?> managementFactory = ReflectionUtils
final Class<?> managementFactory = reflectionUtils
.classForNameOrNull("java.lang.management.ManagementFactory");
final Object runtimeMXBean = managementFactory == null ? null
: ReflectionUtils.invokeStaticMethod(/* throwException = */ false, managementFactory,
: reflectionUtils.invokeStaticMethod(/* throwException = */ false, managementFactory,
"getRuntimeMXBean");
@SuppressWarnings("unchecked")
final List<String> commandlineArguments = runtimeMXBean == null ? null
: (List<String>) ReflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean,
: (List<String>) reflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean,
"getInputArguments");
if (commandlineArguments != null) {
for (final String arg : commandlineArguments) {
Expand Down
34 changes: 18 additions & 16 deletions src/main/java/io/github/classgraph/ModuleReaderProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,7 @@ public class ModuleReaderProxy implements Closeable {
/** Collector<Object, ?, List<Object>> collectorsToList = Collectors.toList(); */
private static Object collectorsToList;

static {
collectorClass = ReflectionUtils.classForNameOrNull("java.util.stream.Collector");
final Class<?> collectorsClass = ReflectionUtils.classForNameOrNull("java.util.stream.Collectors");
if (collectorsClass != null) {
collectorsToList = ReflectionUtils.invokeStaticMethod(/* throwException = */ true, collectorsClass,
"toList");
}
}
private ReflectionUtils reflectionUtils;

/**
* Constructor.
Expand All @@ -66,7 +59,16 @@ public class ModuleReaderProxy implements Closeable {
*/
ModuleReaderProxy(final ModuleRef moduleRef) throws IOException {
try {
moduleReader = (AutoCloseable) ReflectionUtils.invokeMethod(/* throwException = */ true,
reflectionUtils = moduleRef.reflectionUtils;
if (collectorClass == null || collectorsToList == null) {
collectorClass = reflectionUtils.classForNameOrNull("java.util.stream.Collector");
final Class<?> collectorsClass = reflectionUtils.classForNameOrNull("java.util.stream.Collectors");
if (collectorsClass != null) {
collectorsToList = reflectionUtils.invokeStaticMethod(/* throwException = */ true,
collectorsClass, "toList");
}
}
moduleReader = (AutoCloseable) reflectionUtils.invokeMethod(/* throwException = */ true,
moduleRef.getReference(), "open");
if (moduleReader == null) {
throw new IllegalArgumentException("moduleReference.open() should not return null");
Expand Down Expand Up @@ -104,12 +106,12 @@ public List<String> list() throws SecurityException {
if (collectorsToList == null) {
throw new IllegalArgumentException("Could not call Collectors.toList()");
}
final Object /* Stream<String> */ resourcesStream = ReflectionUtils
final Object /* Stream<String> */ resourcesStream = reflectionUtils
.invokeMethod(/* throwException = */ true, moduleReader, "list");
if (resourcesStream == null) {
throw new IllegalArgumentException("Could not call moduleReader.list()");
}
final Object resourcesList = ReflectionUtils.invokeMethod(/* throwException = */ true, resourcesStream,
final Object resourcesList = reflectionUtils.invokeMethod(/* throwException = */ true, resourcesStream,
"collect", collectorClass, collectorsToList);
if (resourcesList == null) {
throw new IllegalArgumentException("Could not call moduleReader.list().collect(Collectors.toList())");
Expand All @@ -132,12 +134,12 @@ public List<String> list() throws SecurityException {
* If the module cannot be accessed.
*/
public InputStream open(final String path) throws SecurityException {
final Object /* Optional<InputStream> */ optionalInputStream = ReflectionUtils
final Object /* Optional<InputStream> */ optionalInputStream = reflectionUtils
.invokeMethod(/* throwException = */ true, moduleReader, "open", String.class, path);
if (optionalInputStream == null) {
throw new IllegalArgumentException("Got null result from ModuleReader#open for path " + path);
}
final InputStream inputStream = (InputStream) ReflectionUtils.invokeMethod(/* throwException = */ true,
final InputStream inputStream = (InputStream) reflectionUtils.invokeMethod(/* throwException = */ true,
optionalInputStream, "get");
if (inputStream == null) {
throw new IllegalArgumentException("Got null result from ModuleReader#open(String)#get()");
Expand All @@ -158,12 +160,12 @@ public InputStream open(final String path) throws SecurityException {
* if the resource is larger than 2GB, the maximum capacity of a byte buffer.
*/
public ByteBuffer read(final String path) throws SecurityException, OutOfMemoryError {
final Object /* Optional<ByteBuffer> */ optionalByteBuffer = ReflectionUtils
final Object /* Optional<ByteBuffer> */ optionalByteBuffer = reflectionUtils
.invokeMethod(/* throwException = */ true, moduleReader, "read", String.class, path);
if (optionalByteBuffer == null) {
throw new IllegalArgumentException("Got null result from ModuleReader#read(String)");
}
final ByteBuffer byteBuffer = (ByteBuffer) ReflectionUtils.invokeMethod(/* throwException = */ true,
final ByteBuffer byteBuffer = (ByteBuffer) reflectionUtils.invokeMethod(/* throwException = */ true,
optionalByteBuffer, "get");
if (byteBuffer == null) {
throw new IllegalArgumentException("Got null result from ModuleReader#read(String).get()");
Expand All @@ -178,7 +180,7 @@ public ByteBuffer read(final String path) throws SecurityException, OutOfMemoryE
* The {@link ByteBuffer} to release.
*/
public void release(final ByteBuffer byteBuffer) {
ReflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "release", ByteBuffer.class,
reflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "release", ByteBuffer.class,
byteBuffer);
}
}
26 changes: 15 additions & 11 deletions src/main/java/io/github/classgraph/ModuleRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public class ModuleRef implements Comparable<ModuleRef> {
/** The ClassLoader that loads classes in the module. May be null, to represent the bootstrap classloader. */
private final ClassLoader classLoader;

ReflectionUtils reflectionUtils;

/**
* Constructor.
*
Expand All @@ -78,7 +80,8 @@ public class ModuleRef implements Comparable<ModuleRef> {
* @param moduleLayer
* The module layer, of JPMS type ModuleLayer
*/
public ModuleRef(final Object moduleReference, final Object moduleLayer) {
public ModuleRef(final Object moduleReference, final Object moduleLayer,
final ReflectionUtils reflectionUtils) {
if (moduleReference == null) {
throw new IllegalArgumentException("moduleReference cannot be null");
}
Expand All @@ -87,46 +90,47 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) {
}
this.reference = moduleReference;
this.layer = moduleLayer;
this.reflectionUtils = reflectionUtils;

this.descriptor = ReflectionUtils.invokeMethod(/* throwException = */ true, moduleReference, "descriptor");
this.descriptor = reflectionUtils.invokeMethod(/* throwException = */ true, moduleReference, "descriptor");
if (this.descriptor == null) {
// Should not happen
throw new IllegalArgumentException("moduleReference.descriptor() should not return null");
}
this.name = (String) ReflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor, "name");
this.name = (String) reflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor, "name");
@SuppressWarnings("unchecked")
final Set<String> modulePackages = (Set<String>) ReflectionUtils.invokeMethod(/* throwException = */ true,
final Set<String> modulePackages = (Set<String>) reflectionUtils.invokeMethod(/* throwException = */ true,
this.descriptor, "packages");
if (modulePackages == null) {
// Should not happen
throw new IllegalArgumentException("moduleReference.descriptor().packages() should not return null");
}
this.packages = new ArrayList<>(modulePackages);
CollectionUtils.sortIfNotEmpty(this.packages);
final Object optionalRawVersion = ReflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor,
final Object optionalRawVersion = reflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor,
"rawVersion");
if (optionalRawVersion != null) {
final Boolean isPresent = (Boolean) ReflectionUtils.invokeMethod(/* throwException = */ true,
final Boolean isPresent = (Boolean) reflectionUtils.invokeMethod(/* throwException = */ true,
optionalRawVersion, "isPresent");
if (isPresent != null && isPresent) {
this.rawVersion = (String) ReflectionUtils.invokeMethod(/* throwException = */ true,
this.rawVersion = (String) reflectionUtils.invokeMethod(/* throwException = */ true,
optionalRawVersion, "get");
}
}
final Object moduleLocationOptional = ReflectionUtils.invokeMethod(/* throwException = */ true,
final Object moduleLocationOptional = reflectionUtils.invokeMethod(/* throwException = */ true,
moduleReference, "location");
if (moduleLocationOptional == null) {
// Should not happen
throw new IllegalArgumentException("moduleReference.location() should not return null");
}
final Object moduleLocationIsPresent = ReflectionUtils.invokeMethod(/* throwException = */ true,
final Object moduleLocationIsPresent = reflectionUtils.invokeMethod(/* throwException = */ true,
moduleLocationOptional, "isPresent");
if (moduleLocationIsPresent == null) {
// Should not happen
throw new IllegalArgumentException("moduleReference.location().isPresent() should not return null");
}
if ((Boolean) moduleLocationIsPresent) {
this.location = (URI) ReflectionUtils.invokeMethod(/* throwException = */ true, moduleLocationOptional,
this.location = (URI) reflectionUtils.invokeMethod(/* throwException = */ true, moduleLocationOptional,
"get");
if (this.location == null) {
// Should not happen
Expand All @@ -137,7 +141,7 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) {
}

// Find the classloader for the module
this.classLoader = (ClassLoader) ReflectionUtils.invokeMethod(/* throwException = */ true, moduleLayer,
this.classLoader = (ClassLoader) reflectionUtils.invokeMethod(/* throwException = */ true, moduleLayer,
"findLoader", String.class, this.name);
}

Expand Down
12 changes: 7 additions & 5 deletions src/main/java/io/github/classgraph/ScanResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ public final class ScanResult implements Closeable, AutoCloseable {
/** If true, this ScanResult has already been closed. */
private final AtomicBoolean closed = new AtomicBoolean(false);

protected ReflectionUtils reflectionUtils;

/** The toplevel log. */
private final LogNode topLevelLog;

Expand Down Expand Up @@ -206,15 +208,15 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s
/**
* Static initialization (warm up classloading), called when the ClassGraph class is initialized.
*/
static void init() {
static void init(final ReflectionUtils reflectionUtils) {
if (!initialized.getAndSet(true)) {
// Pre-load non-system classes necessary for calling scanResult.close(), so that classes that need
// to be loaded to close resources are already loaded and cached. This was originally for use in
// a shutdown hook (#331), which has now been removed, but it is probably still a good idea to
// ensure that classes needed to unmap DirectByteBuffer instances are available at init.
// We achieve this by mmap'ing a file and then closing it, since the only problematic classes are
// the PriviledgedAction anonymous inner classes used by FileUtils::closeDirectByteBuffer.
FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null);
FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), reflectionUtils, /* log = */ null);
}
}

Expand Down Expand Up @@ -260,6 +262,7 @@ static void init() {
this.packageNameToPackageInfo = packageNameToPackageInfo;
this.moduleNameToModuleInfo = moduleNameToModuleInfo;
this.nestedJarHandler = nestedJarHandler;
this.reflectionUtils = nestedJarHandler.reflectionUtils;
this.topLevelLog = topLevelLog;

if (classNameToClassInfo != null) {
Expand Down Expand Up @@ -450,7 +453,7 @@ public List<ModuleRef> getModules() {
* @return The {@link ModulePathInfo}.
*/
public ModulePathInfo getModulePathInfo() {
scanSpec.modulePathInfo.getRuntimeInfo();
scanSpec.modulePathInfo.getRuntimeInfo(reflectionUtils);
return scanSpec.modulePathInfo;
}

Expand Down Expand Up @@ -1591,12 +1594,11 @@ public void close() {
}
classGraphClassLoader = null;
classpathFinder = null;
reflectionUtils = null;
// Flush log on exit, in case additional log entries were generated after scan() completed
if (topLevelLog != null) {
topLevelLog.flush();
}
// Unload the reflection driver (#756)
ReflectionUtils.unloadReflectionDriver();
}
}

Expand Down
Loading

0 comments on commit 2bb3192

Please sign in to comment.