From 8085c72543b9dfbb8bd21ad1b6dc788e722e24cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 02:01:50 +0000 Subject: [PATCH 001/713] Bump maven-gpg-plugin from 1.6 to 3.0.1 Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.0.1. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.0.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5fa4b55c9..5c76e2ced 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 org.sonatype.plugins From 02f3d5d67aa1f8ccae7bf1bf40b1743ed706fcdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 03:01:24 +0000 Subject: [PATCH 002/713] Bump actions/checkout from 2 to 2.3.4 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.3.4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v2.3.4) Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ef408cde..cc057d788 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '8', '11', '12', '13', '14', '15', '16' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK uses: actions/setup-java@v2 with: From d83ed1ed185dbc924132ac97d6b3273dbebd0a22 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 May 2021 12:29:09 -0600 Subject: [PATCH 003/713] Fixes for determination of current dir (#520) --- .../classgraph/ClasspathElementZip.java | 6 +- .../java/io/github/classgraph/Scanner.java | 4 +- .../classgraph/classpath/ClasspathFinder.java | 2 +- .../classgraph/classpath/ClasspathOrder.java | 12 ++-- .../classgraph/classpath/SystemJarFinder.java | 4 +- .../fastzipfilereader/PhysicalZipFile.java | 4 +- .../io/github/classgraph/utils/FileUtils.java | 61 +++++++++++++------ 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 01eb1394a..f675a52e5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -144,7 +144,7 @@ void open(final WorkQueue workQueue, final LogNode log) } final LogNode subLog = log == null ? null : log(classpathElementIdx, "Opening jar: " + rawPath, log); final int plingIdx = rawPath.indexOf('!'); - final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), plingIdx < 0 ? rawPath : rawPath.substring(0, plingIdx)); if (!scanSpec.jarAcceptReject.isAcceptedAndNotRejected(outermostZipFilePathResolved)) { if (subLog != null) { @@ -171,7 +171,7 @@ void open(final WorkQueue workQueue, final LogNode log) } // Get the normalized path of the logical zipfile - zipFilePath = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, logicalZipFile.getPath()); + zipFilePath = FastPathResolver.resolve(FileUtils.currDirPath(), logicalZipFile.getPath()); // Get package root of jarfile final String packageRoot = logicalZipFileAndPackageRoot.getValue(); @@ -723,7 +723,7 @@ File getFile() { } else { // Not performing a full scan (only getting classpath elements), so logicalZipFile is not set final int plingIdx = rawPath.indexOf('!'); - final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), plingIdx < 0 ? rawPath : rawPath.substring(0, plingIdx)); return new File(outermostZipFilePathResolved); } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 6f452d552..45cca47e0 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -493,7 +493,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa final String classpathEntryPathStr = classpathEntryObj.toString(); // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators - final String pathNormalized = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String pathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), classpathEntryPathStr); // Strip everything after first "!", to get path of base jarfile or dir @@ -521,7 +521,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa throw new IOException("Not a normal file or directory"); } // Check if canonicalized path is the same as pre-canonicalized path - final String baseFileCanonicalPathNormalized = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String baseFileCanonicalPathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), fileCanonicalized.getPath()); final String canonicalPathNormalized = plingIdx < 0 ? baseFileCanonicalPathNormalized : baseFileCanonicalPathNormalized + pathNormalized.substring(plingIdx); diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 16c727158..aa74ba9cb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -292,7 +292,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { : classpathFinderLog.log("Getting classpath entries from java.class.path"); for (final String pathElement : pathElements) { // pathElement is not also listed in an ignored parent classloader - final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String pathElementResolved = FastPathResolver.resolve(FileUtils.currDirPath(), pathElement); classpathOrder.addClasspathEntry(pathElementResolved, defaultClassLoader, scanSpec, sysPropLog); } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 97650ffb4..edff4e036 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -200,7 +200,7 @@ private boolean filter(final URL classpathElementURL, final String classpathElem * * @param pathEntry * the system classpath entry -- the path string should already have been run through - * FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, path) + * FastPathResolver.resolve(FileUtils.currDirPath(), path) * @param classLoader * the classloader * @return true, if added and unique @@ -263,7 +263,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle return true; } } else { - final String pathElementStrResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String pathElementStrResolved = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStrWithoutSuffix); if (scanSpec.overrideClasspath == null // && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStrResolved) @@ -304,7 +304,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped String pathElementStr = pathElement instanceof Path ? ((Path) pathElement).toUri().toString() : pathElement.toString(); - pathElementStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElementStr); + pathElementStr = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStr); if (pathElementStr.isEmpty()) { return false; } @@ -365,7 +365,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Apply classpath element filters, if any final String baseDirPath = pathElementStr.length() == 1 ? "" : pathElementStr.substring(0, pathElementStr.length() - 2); - final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), baseDirPath); if (!filter(pathElementURL, baseDirPath) || (!baseDirPathResolved.equals(baseDirPath) && !filter(pathElementURL, baseDirPathResolved))) { @@ -408,7 +408,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Add each directory entry as a classpath element final String fileInDirPath = fileInDir.getPath(); final String fileInDirPathResolved = FastPathResolver - .resolve(FileUtils.CURR_DIR_PATH, fileInDirPath); + .resolve(FileUtils.currDirPath(), fileInDirPath); if (addClasspathEntry(fileInDirPathResolved, fileInDirPathResolved, classLoader, scanSpec)) { if (dirLog != null) { @@ -438,7 +438,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla } } else { // Non-wildcarded (standard) classpath element - final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + final String pathElementResolved = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStr); if (!filter(pathElementURL, pathElementStr) || (!pathElementResolved.equals(pathElementStr) && !filter(pathElementURL, pathElementResolved))) { diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java index be2116d8e..3e51b6478 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -70,7 +70,7 @@ private static boolean addJREPath(final File dir) { for (final File file : dirFiles) { final String filePath = file.getPath(); if (filePath.endsWith(".jar")) { - final String jarPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, filePath); + final String jarPathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), filePath); if (jarPathResolved.endsWith("/rt.jar")) { RT_JARS.add(jarPathResolved); } else { @@ -81,7 +81,7 @@ private static boolean addJREPath(final File dir) { final String canonicalFilePath = canonicalFile.getPath(); if (!canonicalFilePath.equals(filePath)) { final String canonicalJarPathResolved = FastPathResolver - .resolve(FileUtils.CURR_DIR_PATH, filePath); + .resolve(FileUtils.currDirPath(), filePath); JRE_LIB_OR_EXT_JARS.add(canonicalJarPathResolved); } } catch (IOException | SecurityException e) { diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java index b06781e7c..d4b99638f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -83,7 +83,7 @@ class PhysicalZipFile { FileUtils.checkCanReadAndIsFile(file); this.file = file; - this.pathStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); + this.pathStr = FastPathResolver.resolve(FileUtils.currDirPath(), file.getPath()); this.slice = new FileSlice(file, nestedJarHandler, log); } @@ -107,7 +107,7 @@ class PhysicalZipFile { FileUtils.checkCanReadAndIsFile(path); this.path = path; - this.pathStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, path.toString()); + this.pathStr = FastPathResolver.resolve(FileUtils.currDirPath(), path.toString()); this.slice = new PathSlice(path, nestedJarHandler); } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index b697e4172..744560c61 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -30,12 +30,15 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.IOError; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; @@ -73,7 +76,7 @@ public final class FileUtils { * The current directory path (only reads the current directory once, the first time this field is accessed, so * will not reflect subsequent changes to the current directory). */ - public static final String CURR_DIR_PATH; + private static String currDirPath; /** * The maximum size of a file buffer array. Eight bytes smaller than {@link Integer#MAX_VALUE}, since some VMs @@ -90,23 +93,47 @@ private FileUtils() { // Cannot be constructed } - static { - String currDirPathStr = ""; - try { - // The result is moved to currDirPathStr after each step, so we can provide fine-grained debug info and - // a best guess at the path, if the current dir doesn't exist (#109), or something goes wrong while - // trying to get the current dir path. - Path currDirPath = Paths.get("").toAbsolutePath(); - currDirPathStr = currDirPath.toString(); - currDirPath = currDirPath.normalize(); - currDirPathStr = currDirPath.toString(); - currDirPath = currDirPath.toRealPath(LinkOption.NOFOLLOW_LINKS); - currDirPathStr = currDirPath.toString(); - currDirPathStr = FastPathResolver.resolve(currDirPathStr); - } catch (final IOException e) { - throw new RuntimeException("Could not resolve current directory: " + currDirPathStr, e); + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the current directory (only looks at the current directory the first time it is called, then caches this + * value for future reads). + * + * @return The current directory, as a string + */ + public static String currDirPath() { + if (currDirPath == null) { + String currDirPathStr = ""; + // The result is moved to currDirPathStr after each step, so we can provide fine-grained debug info + // and a best guess at the path, if the current dir doesn't exist (#109), or something goes wrong + // while trying to get the current dir path. + Path path = null; + try { + path = Paths.get(""); + } catch (final InvalidPathException e) { + try { + path = FileSystems.getDefault().getPath("."); + } catch (final InvalidPathException e2) { + // Fall through + } + } + if (path != null) { + try { + currDirPathStr = path.toAbsolutePath().normalize().toRealPath(LinkOption.NOFOLLOW_LINKS) + .toString(); + } catch (IOError | SecurityException | IOException e) { + // Fall through + } + } + if (currDirPathStr.isEmpty()) { + currDirPathStr = System.getProperty("user.dir"); + if (currDirPathStr.isEmpty()) { + currDirPathStr = "."; + } + } + currDirPath = FastPathResolver.resolve(currDirPathStr); } - CURR_DIR_PATH = currDirPathStr; + return currDirPath; } // ------------------------------------------------------------------------------------------------------------- From 3e8086aca78ae93b1f91f7a43b86bd748533548d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 May 2021 12:31:01 -0600 Subject: [PATCH 004/713] Remove out-of-date comment (#520) --- src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 744560c61..7ed18aff7 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -104,9 +104,6 @@ private FileUtils() { public static String currDirPath() { if (currDirPath == null) { String currDirPathStr = ""; - // The result is moved to currDirPathStr after each step, so we can provide fine-grained debug info - // and a best guess at the path, if the current dir doesn't exist (#109), or something goes wrong - // while trying to get the current dir path. Path path = null; try { path = Paths.get(""); From d3b27fa4df72c40a96c16a76f64b48da2ce5cc65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 May 2021 02:01:00 +0000 Subject: [PATCH 005/713] Bump jmh-generator-annprocess from 1.29 to 1.31 Bumps jmh-generator-annprocess from 1.29 to 1.31. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c76e2ced..6d438dbab 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.29 + 1.31 test From e057b61d17b677ee6a7a837ab97f3c5334dc34a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 May 2021 02:01:03 +0000 Subject: [PATCH 006/713] Bump jmh-core from 1.29 to 1.31 Bumps jmh-core from 1.29 to 1.31. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c76e2ced..ed8cebb86 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.29 + 1.31 test From 94d19f2df49a44948ef469284d4352bb11c37f51 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 03:13:16 -0600 Subject: [PATCH 007/713] Fix Javadoc --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 9e283f045..200dd8389 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -225,7 +225,7 @@ private static List> getReverseMethodAttemptOrder(final Class cls) { * The value of the first parameter to invoke the method with. * @param throwException * If true, throw an exception if the field value could not be read. - * @return The field value. + * @return The result of the method invocation. * @throws IllegalArgumentException * If the field value could not be read. */ @@ -285,7 +285,7 @@ private static Object invokeMethod(final Class cls, final Object obj, final S * The method name. * @param throwException * If true, throw an exception if the field value could not be read. - * @return The field value. + * @return The result of the method invocation. * @throws IllegalArgumentException * If the field value could not be read. */ From 7bda285b19c18dcdfb3cfb9be0dafe83d43db61e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 03:17:06 -0600 Subject: [PATCH 008/713] Attempt CxfContainerClassLoaderHandler (#515) --- .../CxfContainerClassLoaderHandler.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java new file mode 100644 index 000000000..186877c70 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -0,0 +1,96 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.classloaderhandler; + +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; +import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.ReflectionUtils; + +/** ClassLoaderHandler that is able to extract the URLs from a CxfContainerClassLoader. */ +class CxfContainerClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private CxfContainerClassLoaderHandler() { + } + + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. + * @param log + * the log + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. + */ + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + return "org.apache.openejb.server.cxf.transport.util.CxfContainerClassLoader" + .equals(classLoaderClass.getName()); + } + + /** + * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the order for. + * @param classLoaderOrder + * a {@link ClassLoaderOrder} object to update. + * @param log + * the log + */ + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + try { + classLoaderOrder.delegateTo( + Class.forName("org.apache.openejb.server.cxf.transport.util.CxfUtil").getClassLoader(), + /* isParent = */ true, log); + } catch (LinkageError | ClassNotFoundException e) { + // Ignore + } + classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(classLoader, "tccl", false), + /* isParent = */ false, log); + } + + /** + * Find the classpath entries for the associated {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the classpath entries order for. + * @param classpathOrder + * a {@link ClasspathOrder} object to update. + * @param scanSpec + * the {@link ScanSpec}. + * @param log + * the log. + */ + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // Classloader doesn't do any classloading of its own, it only delegates to one or two other classloaders + } +} From b3ef4effa80ee43f7a143dcf4dbc309c3cb86319 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 04:00:07 -0600 Subject: [PATCH 009/713] Attempt at TomEEWebappClassLoader (#515) --- .../TomEEWebappClassLoaderHandler.java | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java new file mode 100644 index 000000000..5e53a47d4 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java @@ -0,0 +1,115 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.classloaderhandler; + +import java.util.Iterator; + +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; +import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.ReflectionUtils; + +/** ClassLoaderHandler that is able to extract the URLs from a TomEEWebappClassLoader. */ +class TomEEWebappClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private TomEEWebappClassLoaderHandler() { + } + + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. + * @param log + * the log + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. + */ + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + return "org.apache.tomee.catalina.TomEEWebappClassLoader".equals(classLoaderClass.getName()); + } + + /** + * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the order for. + * @param classLoaderOrder + * a {@link ClassLoaderOrder} object to update. + * @param log + * the log + */ + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + // TomEEWebappClassLoader has a lot of complex delegation rules, including classname-specific delegation, + // which is not supported by the current ClassGraph model, so we just try to approximate the delegation + // order with a fixed order here. + try { + classLoaderOrder.delegateTo(Class.forName("org.apache.openejb.OpenEJB").getClassLoader(), + /* isParent = */ true, log); + } catch (LinkageError | ClassNotFoundException e) { + // Ignore + } + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); + } + + /** + * Find the classpath entries for the associated {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the classpath entries order for. + * @param classpathOrder + * a {@link ClasspathOrder} object to update. + * @param scanSpec + * the {@link ScanSpec}. + * @param log + * the log. + */ + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // Type WebResourceRoot + final Object resources = ReflectionUtils.getFieldVal(classLoader, "resources", false); + if (resources != null) { + // Type List + final Object jars = ReflectionUtils.getFieldVal(resources, "jarResources", false); + if (jars != null && jars instanceof Iterable) { + final Iterable it = (Iterable) jars; + // Type Iterator + final Iterator jarIt = it.iterator(); + while (jarIt.hasNext()) { + // Type WebResourceSet + final Object set = jarIt.next(); + final Object baseUrl = ReflectionUtils.invokeMethod(set, "getBaseUrl", false); + classpathOrder.addClasspathEntry(baseUrl, classLoader, scanSpec, log); + } + } + } + } +} From 5a941fa7dab9e67bf91aaaf0170e17f26eb721fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 10:31:09 -0600 Subject: [PATCH 010/713] Some more tweaks to curr dir determination code (#520) --- .../io/github/classgraph/utils/FileUtils.java | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 7ed18aff7..18bbde4ac 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -36,7 +36,6 @@ import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.LinkOption; @@ -103,32 +102,33 @@ private FileUtils() { */ public static String currDirPath() { if (currDirPath == null) { - String currDirPathStr = ""; - Path path = null; - try { - path = Paths.get(""); - } catch (final InvalidPathException e) { - try { - path = FileSystems.getDefault().getPath("."); - } catch (final InvalidPathException e2) { - // Fall through - } - } - if (path != null) { + // user.dir should be the current directory at the time the JVM is started, which is + // where classpath elements should be resolved relative to + String currDirPathStr = System.getProperty("user.dir"); + // user.dir should probbly always be set. But just in case it is not, try reading the + // actual current directory at the time ClassGraph is first invoked. + if (currDirPathStr == null) { + Path path = null; try { - currDirPathStr = path.toAbsolutePath().normalize().toRealPath(LinkOption.NOFOLLOW_LINKS) - .toString(); - } catch (IOError | SecurityException | IOException e) { - // Fall through + path = Paths.get(""); + } catch (final InvalidPathException e1) { + try { + path = Paths.get("."); + } catch (final InvalidPathException e2) { + // Fall through + } } - } - if (currDirPathStr.isEmpty()) { - currDirPathStr = System.getProperty("user.dir"); - if (currDirPathStr.isEmpty()) { - currDirPathStr = "."; + if (path != null) { + try { + currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); + } catch (IOError | SecurityException | IOException e) { + // Fall through + } } } - currDirPath = FastPathResolver.resolve(currDirPathStr); + // Normalize current directory the same way all other paths are normalized in ClassGraph, + // for consistency + currDirPath = currDirPathStr == null ? "" : FastPathResolver.resolve(currDirPathStr); } return currDirPath; } From 650f446f0b13081852e140efaa1be16eade4fd3f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 10:37:13 -0600 Subject: [PATCH 011/713] More robustness fixes to curr dir determination (#520) --- .../io/github/classgraph/utils/FileUtils.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 18bbde4ac..e5f0c9a97 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -104,11 +104,17 @@ public static String currDirPath() { if (currDirPath == null) { // user.dir should be the current directory at the time the JVM is started, which is // where classpath elements should be resolved relative to + Path path = null; String currDirPathStr = System.getProperty("user.dir"); - // user.dir should probbly always be set. But just in case it is not, try reading the - // actual current directory at the time ClassGraph is first invoked. - if (currDirPathStr == null) { - Path path = null; + if (currDirPathStr != null) { + try { + path = Paths.get(currDirPathStr); + } catch (final InvalidPathException e2) { + // Fall through + } + } else { + // user.dir should probably always be set. But just in case it is not, try reading the + // actual current directory at the time ClassGraph is first invoked. try { path = Paths.get(""); } catch (final InvalidPathException e1) { @@ -118,12 +124,13 @@ public static String currDirPath() { // Fall through } } - if (path != null) { - try { - currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); - } catch (IOError | SecurityException | IOException e) { - // Fall through - } + } + // Try normalizing path + if (path != null) { + try { + currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); + } catch (IOError | SecurityException | IOException e) { + // Fall through } } // Normalize current directory the same way all other paths are normalized in ClassGraph, From 72c87315e99e1f3a09d71899b16d0320462507f4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 May 2021 10:49:30 -0600 Subject: [PATCH 012/713] Do not add parent packages if they are not accepted packages (#510) --- .../java/io/github/classgraph/Classfile.java | 2 +- .../io/github/classgraph/PackageInfo.java | 27 ++++++++++++------- .../issues/issue107/Issue107Test.java | 7 +++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index e24b8f5f5..7d0f6c60b 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -523,7 +523,7 @@ void link(final Map classNameToClassInfo, if (!isModuleDescriptor) { // Get package for this class or package descriptor final String packageName = PackageInfo.getParentPackageName(className); - packageInfo = PackageInfo.getOrCreatePackage(packageName, packageNameToPackageInfo); + packageInfo = PackageInfo.getOrCreatePackage(packageName, packageNameToPackageInfo, scanSpec); if (isPackageDescriptor) { // Add any class annotations on the package-info.class file to the ModuleInfo packageInfo.addAnnotations(classAnnotations); diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 15d2269ba..ed7f0b604 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; /** Holds metadata about a package encountered during a scan. */ @@ -270,10 +271,12 @@ static String getParentPackageName(final String packageOrClassName) { * the package name * @param packageNameToPackageInfo * a map from package name to package info + * @param scanSpec + * the ScanSpec. * @return the {@link PackageInfo} for the named package. */ static PackageInfo getOrCreatePackage(final String packageName, - final Map packageNameToPackageInfo) { + final Map packageNameToPackageInfo, final ScanSpec scanSpec) { // Get or create PackageInfo object for this package PackageInfo packageInfo = packageNameToPackageInfo.get(packageName); if (packageInfo != null) { @@ -287,16 +290,20 @@ static PackageInfo getOrCreatePackage(final String packageName, // If this is not the root package ("") if (!packageName.isEmpty()) { // Recursively create PackageInfo objects for parent packages (until a parent package that already - // exists is reached), and connect each ancestral package to its parent - final PackageInfo parentPackageInfo = getOrCreatePackage(getParentPackageName(packageInfo.name), - packageNameToPackageInfo); - if (parentPackageInfo != null) { - // Link package to parent - if (parentPackageInfo.children == null) { - parentPackageInfo.children = new HashSet<>(); + // exists or that is not accepted is reached), and connect each ancestral package to its parent + final String parentPackageName = getParentPackageName(packageInfo.name); + if (scanSpec.packageAcceptReject.isAcceptedAndNotRejected(parentPackageName) + || scanSpec.packagePrefixAcceptReject.isAcceptedAndNotRejected(parentPackageName)) { + final PackageInfo parentPackageInfo = getOrCreatePackage(parentPackageName, + packageNameToPackageInfo, scanSpec); + if (parentPackageInfo != null) { + // Link package to parent + if (parentPackageInfo.children == null) { + parentPackageInfo.children = new HashSet<>(); + } + parentPackageInfo.children.add(packageInfo); + packageInfo.parent = parentPackageInfo; } - parentPackageInfo.children.add(packageInfo); - packageInfo.parent = parentPackageInfo; } } diff --git a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java index 629848cac..a53864c14 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -47,14 +47,13 @@ public class Issue107Test { @Test public void issue107Test() { // Package annotations should have "package-info" as their class name - final String pkg = Issue107Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg).enableAnnotationInfo() + try (ScanResult scanResult = new ClassGraph().acceptPackages("io.github.classgraph").enableAnnotationInfo() // package-info is a non-public class .ignoreClassVisibility() // .scan()) { assertThat(scanResult.getClassesWithAnnotation(PackageAnnotation.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getPackageInfo().getNames()).containsAll(Arrays.asList("", "io", "io.github", - "io.github.classgraph", Issue107Test.class.getPackage().getName())); + assertThat(scanResult.getPackageInfo().getNames()) + .containsAll(Arrays.asList("io.github.classgraph", Issue107Test.class.getPackage().getName())); assertThat(scanResult.getPackageInfo(Issue107Test.class.getPackage().getName()).getAnnotationInfo() .getNames()).containsOnly(PackageAnnotation.class.getName()); } From 701c263fa6c9e4f02bcfca406efe97a6d2dcc59a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 May 2021 01:39:29 -0600 Subject: [PATCH 013/713] More robustness fixes for curr dir (#520) --- .../io/github/classgraph/utils/FileUtils.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index e5f0c9a97..5471958d1 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -109,29 +109,45 @@ public static String currDirPath() { if (currDirPathStr != null) { try { path = Paths.get(currDirPathStr); - } catch (final InvalidPathException e2) { + if (!path.toFile().canRead()) { + path = null; + } + } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { // Fall through } - } else { + } + if (path == null) { // user.dir should probably always be set. But just in case it is not, try reading the // actual current directory at the time ClassGraph is first invoked. try { path = Paths.get(""); - } catch (final InvalidPathException e1) { - try { - path = Paths.get("."); - } catch (final InvalidPathException e2) { - // Fall through + if (!path.toFile().canRead()) { + path = null; + } + } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { + // Fall through + } + } + if (path == null) { + try { + path = Paths.get("."); + if (!path.toFile().canRead()) { + path = null; } + } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { + // Fall through } } // Try normalizing path if (path != null) { + currDirPathStr = null; try { currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); } catch (IOError | SecurityException | IOException e) { // Fall through } + } else { + currDirPathStr = ""; } // Normalize current directory the same way all other paths are normalized in ClassGraph, // for consistency From c9ac99c4255154530efe6de44d029bc598f919d1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 May 2021 01:40:14 -0600 Subject: [PATCH 014/713] Simplify code (#520) --- .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 5471958d1..b43e6675a 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -139,19 +139,17 @@ public static String currDirPath() { } } // Try normalizing path + currDirPathStr = ""; if (path != null) { - currDirPathStr = null; try { currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); } catch (IOError | SecurityException | IOException e) { // Fall through } - } else { - currDirPathStr = ""; } // Normalize current directory the same way all other paths are normalized in ClassGraph, // for consistency - currDirPath = currDirPathStr == null ? "" : FastPathResolver.resolve(currDirPathStr); + currDirPath = FastPathResolver.resolve(currDirPathStr); } return currDirPath; } From de6e8c07aed0454a015b419cc4793c019851fbde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 02:00:57 +0000 Subject: [PATCH 015/713] Bump junit-jupiter from 5.7.1 to 5.7.2 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.1...r5.7.2) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be2446a77..35c637240 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.junit.jupiter junit-jupiter - 5.7.1 + 5.7.2 test From e5cab34121484a1a19cb04b67baebca06f4c2e8b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 17 May 2021 02:17:40 -0600 Subject: [PATCH 016/713] Remove unnecessary code (#520) --- .../nonapi/io/github/classgraph/utils/FileUtils.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index b43e6675a..f78eabc19 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -128,16 +128,6 @@ public static String currDirPath() { // Fall through } } - if (path == null) { - try { - path = Paths.get("."); - if (!path.toFile().canRead()) { - path = null; - } - } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { - // Fall through - } - } // Try normalizing path currDirPathStr = ""; if (path != null) { From 73a31037345a7d2d0ea4228c72152ee9561eb62f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 18 May 2021 16:20:45 -0600 Subject: [PATCH 017/713] Don't access home dir (#520) --- .../io/github/classgraph/utils/FileUtils.java | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index f78eabc19..048ae1a30 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -30,7 +30,6 @@ import java.io.File; import java.io.FileNotFoundException; -import java.io.IOError; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -38,7 +37,6 @@ import java.nio.MappedByteBuffer; import java.nio.file.Files; import java.nio.file.InvalidPathException; -import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; import java.security.AccessController; @@ -109,10 +107,7 @@ public static String currDirPath() { if (currDirPathStr != null) { try { path = Paths.get(currDirPathStr); - if (!path.toFile().canRead()) { - path = null; - } - } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { + } catch (final InvalidPathException e) { // Fall through } } @@ -121,25 +116,14 @@ public static String currDirPath() { // actual current directory at the time ClassGraph is first invoked. try { path = Paths.get(""); - if (!path.toFile().canRead()) { - path = null; - } - } catch (final InvalidPathException | UnsupportedOperationException | SecurityException e) { - // Fall through - } - } - // Try normalizing path - currDirPathStr = ""; - if (path != null) { - try { - currDirPathStr = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); - } catch (IOError | SecurityException | IOException e) { + } catch (final InvalidPathException e) { // Fall through } } + // Normalize current directory the same way all other paths are normalized in ClassGraph, // for consistency - currDirPath = FastPathResolver.resolve(currDirPathStr); + currDirPath = FastPathResolver.resolve(path == null ? "" : path.toString()); } return currDirPath; } From 6ca8afd65ee9e0ddc2f23f7b38e84eb9b0f38a32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 02:00:54 +0000 Subject: [PATCH 018/713] Bump maven-javadoc-plugin from 3.2.0 to 3.3.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.3.0) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 35c637240..56ef75e8d 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.3.0 org.apache.maven.plugins From a9076ccd16637fef9c346ee24b176f7a2e7b7e72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 02:00:58 +0000 Subject: [PATCH 019/713] Bump jmh-core from 1.31 to 1.32 Bumps jmh-core from 1.31 to 1.32. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56ef75e8d..c361e2954 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.31 + 1.32 test From 92dd4d1b324490bab9e8cea9b793cbec275823f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 02:01:00 +0000 Subject: [PATCH 020/713] Bump jmh-generator-annprocess from 1.31 to 1.32 Bumps jmh-generator-annprocess from 1.31 to 1.32. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56ef75e8d..6a1d8879f 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.31 + 1.32 test From 6d7271fd08ca8e420eec83cbcfbbe49c49a5fc0a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 28 May 2021 08:47:38 -0600 Subject: [PATCH 021/713] [maven-release-plugin] prepare release classgraph-4.8.106 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d1a9e4aeb..37c6bd514 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.106-SNAPSHOT + 4.8.106 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.106 From 1a09731506462f2a63e2f377800a676b6c2b6818 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 28 May 2021 08:47:43 -0600 Subject: [PATCH 022/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 37c6bd514..0fbdd1e40 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.106 + 4.8.107-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.106 + classgraph-4.8.105 From 44304a17f91c45e4b7c81919c83e265696a5cbc2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 02:26:22 -0600 Subject: [PATCH 023/713] Merge TomEE ClassLoaderHandler into Tomcat handler (#515) --- .../TomcatWebappClassLoaderBaseHandler.java | 280 ++++++++++-------- 1 file changed, 155 insertions(+), 125 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index 22ff1682e..c263cd0c1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import java.io.File; +import java.util.Iterator; import java.util.List; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; @@ -39,136 +40,165 @@ /** Extract classpath entries from the Tomcat/Catalina WebappClassLoaderBase. */ class TomcatWebappClassLoaderBaseHandler implements ClassLoaderHandler { - /** Class cannot be constructed. */ - private TomcatWebappClassLoaderBaseHandler() { - } + /** Class cannot be constructed. */ + private TomcatWebappClassLoaderBaseHandler() { + } - /** - * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. - * - * @param classLoaderClass - * the {@link ClassLoader} class or one of its superclasses. - * @param log - * the log - * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. - */ - public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); - } + /** + * Check whether this {@link ClassLoaderHandler} can handle a given + * {@link ClassLoader}. + * + * @param classLoaderClass the {@link ClassLoader} class or one of its + * superclasses. + * @param log the log + * @return true if this {@link ClassLoaderHandler} can handle the + * {@link ClassLoader}. + */ + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); + } - /** - * Return true if this classloader delegates to its parent. - * - * @param classLoader - * the {@link ClassLoader}. - * @return true if this classloader delegates to its parent. - */ - private static boolean isParentFirst(final ClassLoader classLoader) { - final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); - if (delegateObject != null) { - return (boolean) delegateObject; - } - // Assume parent-first delegation order - return true; - } + /** + * Return true if this classloader delegates to its parent. + * + * @param classLoader the {@link ClassLoader}. + * @return true if this classloader delegates to its parent. + */ + private static boolean isParentFirst(final ClassLoader classLoader) { + final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); + if (delegateObject != null) { + return (boolean) delegateObject; + } + // Assume parent-first delegation order + return true; + } - /** - * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. - * - * @param classLoader - * the {@link ClassLoader} to find the order for. - * @param classLoaderOrder - * a {@link ClassLoaderOrder} object to update. - * @param log - * the log - */ - public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, - final LogNode log) { - final boolean isParentFirst = isParentFirst(classLoader); - if (isParentFirst) { - // Use parent-first delegation order - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - } - classLoaderOrder.add(classLoader, log); - if (!isParentFirst) { - // Use parent-last delegation order - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - } - } + /** + * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. + * + * @param classLoader the {@link ClassLoader} to find the order for. + * @param classLoaderOrder a {@link ClassLoaderOrder} object to update. + * @param log the log + */ + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + final boolean isParentFirst = isParentFirst(classLoader); + if (isParentFirst) { + // Use parent-first delegation order + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + } + if ("org.apache.tomee.catalina.TomEEWebappClassLoader".equals(classLoader.getClass().getName())) { + // TomEEWebappClassLoader has a lot of complex delegation rules, including classname-specific + // delegation, which is not supported by the current ClassGraph model, so we just try to approximate + // the delegation order with a fixed order. + try { + classLoaderOrder.delegateTo(Class.forName("org.apache.openejb.OpenEJB").getClassLoader(), + /* isParent = */ true, log); + } catch (LinkageError | ClassNotFoundException e) { + // Ignore + } + } + classLoaderOrder.add(classLoader, log); + if (!isParentFirst) { + // Use parent-last delegation order + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + } + } - /** - * Find the classpath entries for the associated {@link ClassLoader}. - * - * @param classLoader - * the {@link ClassLoader} to find the classpath entries order for. - * @param classpathOrder - * a {@link ClasspathOrder} object to update. - * @param scanSpec - * the {@link ScanSpec}. - * @param log - * the log. - */ - public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, - final ScanSpec scanSpec, final LogNode log) { - // type StandardRoot (implements WebResourceRoot) - final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); - // type List - final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); - classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); - // type List> - // members: preResources, mainResources, classResources, jarResources, postResources - @SuppressWarnings("unchecked") - final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", - false); - if (allResources != null) { - // type List - for (final List webResourceSetList : allResources) { - // type WebResourceSet - // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, EmptyResourceSet} - for (final Object webResourceSet : webResourceSetList) { - if (webResourceSet != null) { - // For DirResourceSet - final File file = (File) ReflectionUtils.invokeMethod(webResourceSet, "getFileBase", false); - String base = file == null ? null : file.getPath(); - if (base == null) { - // For FileResourceSet - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBase", false); - } - if (base == null) { - // For JarResourceSet and JarWarResourceSet - // The absolute path to the WAR file on the file system in which the JAR is located - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBaseUrlString", false); - } - if (base != null) { - // For JarWarResourceSet: the path within the WAR file where the JAR file is located - final String archivePath = (String) ReflectionUtils.getFieldVal(webResourceSet, - "archivePath", false); - if (archivePath != null && !archivePath.isEmpty()) { - // If archivePath is non-null, this is a jar within a war - base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); - } - final String className = webResourceSet.getClass().getName(); - final boolean isJar = className - .equals("java.org.apache.catalina.webresources.JarResourceSet") - || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); - // The path within this WebResourceSet where resources will be served from, - // e.g. for a resource JAR, this would be "META-INF/resources" - final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, - "getInternalPath", false); - if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { - classpathOrder.addClasspathEntryObject(base + (isJar ? "!" : "") - + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), - classLoader, scanSpec, log); - } else { - classpathOrder.addClasspathEntryObject(base, classLoader, scanSpec, log); - } - } - } + /** + * Find the classpath entries for the associated {@link ClassLoader}. + * + * @param classLoader the {@link ClassLoader} to find the classpath entries + * order for. + * @param classpathOrder a {@link ClasspathOrder} object to update. + * @param scanSpec the {@link ScanSpec}. + * @param log the log. + */ + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // type StandardRoot (implements WebResourceRoot) + final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); + // type List + final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); + classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); + // type List> + // members: preResources, mainResources, classResources, jarResources, + // postResources + @SuppressWarnings("unchecked") + final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", + false); + if (allResources != null) { + // type List + for (final List webResourceSetList : allResources) { + // type WebResourceSet + // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, + // EmptyResourceSet} + for (final Object webResourceSet : webResourceSetList) { + if (webResourceSet != null) { + // For DirResourceSet + final File file = (File) ReflectionUtils.invokeMethod(webResourceSet, "getFileBase", false); + String base = file == null ? null : file.getPath(); + if (base == null) { + // For FileResourceSet + base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBase", false); + } + if (base == null) { + // For JarResourceSet and JarWarResourceSet + // The absolute path to the WAR file on the file system in which the JAR is + // located + base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBaseUrlString", false); + } + if (base != null) { + // For JarWarResourceSet: the path within the WAR file where the JAR file is + // located + final String archivePath = (String) ReflectionUtils.getFieldVal(webResourceSet, + "archivePath", false); + if (archivePath != null && !archivePath.isEmpty()) { + // If archivePath is non-null, this is a jar within a war + base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); + } + final String className = webResourceSet.getClass().getName(); + final boolean isJar = className + .equals("java.org.apache.catalina.webresources.JarResourceSet") + || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); + // The path within this WebResourceSet where resources will be served from, + // e.g. for a resource JAR, this would be "META-INF/resources" + final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, + "getInternalPath", false); + if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { + classpathOrder.addClasspathEntryObject( + base + (isJar ? "!" : "") + + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), + classLoader, scanSpec, log); + } else { + classpathOrder.addClasspathEntryObject(base, classLoader, scanSpec, log); + } + } + } + } + } + } + // This may or may not duplicate the above + final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); + classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); + + // The following catches a few more jars for org.apache.tomee.catalina.TomEEWebappClassLoader: + // Type WebResourceRoot + final Object resourcesField = ReflectionUtils.getFieldVal(classLoader, "resources", false); + if (resourcesField != null) { + // Type List + final Object jars = ReflectionUtils.getFieldVal(resourcesField, "jarResources", false); + if (jars != null && jars instanceof Iterable) { + final Iterable it = (Iterable) jars; + // Type Iterator + final Iterator jarIt = it.iterator(); + while (jarIt.hasNext()) { + // Type WebResourceSet + final Object set = jarIt.next(); + final Object baseUrl = ReflectionUtils.invokeMethod(set, "getBaseUrl", false); + classpathOrder.addClasspathEntry(baseUrl, classLoader, scanSpec, log); } } } - // This may or may not duplicate the above - final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); - classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); - } + } } From 76eacada22a22f39a791f43f24ce84d670dfa524 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 02:26:52 -0600 Subject: [PATCH 024/713] Remove TomEE handler (#515) --- .../TomEEWebappClassLoaderHandler.java | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java deleted file mode 100644 index 5e53a47d4..000000000 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomEEWebappClassLoaderHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Luke Hutchison - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO - * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ -package nonapi.io.github.classgraph.classloaderhandler; - -import java.util.Iterator; - -import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; -import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; - -/** ClassLoaderHandler that is able to extract the URLs from a TomEEWebappClassLoader. */ -class TomEEWebappClassLoaderHandler implements ClassLoaderHandler { - /** Class cannot be constructed. */ - private TomEEWebappClassLoaderHandler() { - } - - /** - * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. - * - * @param classLoaderClass - * the {@link ClassLoader} class or one of its superclasses. - * @param log - * the log - * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. - */ - public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.tomee.catalina.TomEEWebappClassLoader".equals(classLoaderClass.getName()); - } - - /** - * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. - * - * @param classLoader - * the {@link ClassLoader} to find the order for. - * @param classLoaderOrder - * a {@link ClassLoaderOrder} object to update. - * @param log - * the log - */ - public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, - final LogNode log) { - // TomEEWebappClassLoader has a lot of complex delegation rules, including classname-specific delegation, - // which is not supported by the current ClassGraph model, so we just try to approximate the delegation - // order with a fixed order here. - try { - classLoaderOrder.delegateTo(Class.forName("org.apache.openejb.OpenEJB").getClassLoader(), - /* isParent = */ true, log); - } catch (LinkageError | ClassNotFoundException e) { - // Ignore - } - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - classLoaderOrder.add(classLoader, log); - } - - /** - * Find the classpath entries for the associated {@link ClassLoader}. - * - * @param classLoader - * the {@link ClassLoader} to find the classpath entries order for. - * @param classpathOrder - * a {@link ClasspathOrder} object to update. - * @param scanSpec - * the {@link ScanSpec}. - * @param log - * the log. - */ - public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, - final ScanSpec scanSpec, final LogNode log) { - // Type WebResourceRoot - final Object resources = ReflectionUtils.getFieldVal(classLoader, "resources", false); - if (resources != null) { - // Type List - final Object jars = ReflectionUtils.getFieldVal(resources, "jarResources", false); - if (jars != null && jars instanceof Iterable) { - final Iterable it = (Iterable) jars; - // Type Iterator - final Iterator jarIt = it.iterator(); - while (jarIt.hasNext()) { - // Type WebResourceSet - final Object set = jarIt.next(); - final Object baseUrl = ReflectionUtils.invokeMethod(set, "getBaseUrl", false); - classpathOrder.addClasspathEntry(baseUrl, classLoader, scanSpec, log); - } - } - } - } -} From c7d4e149f00115a71a09c7468ebf2b1980f73608 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 02:31:49 -0600 Subject: [PATCH 025/713] Remove (probably) unnecessary code --- .../TomcatWebappClassLoaderBaseHandler.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index c263cd0c1..d93f5fa1c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -29,7 +29,6 @@ package nonapi.io.github.classgraph.classloaderhandler; import java.io.File; -import java.util.Iterator; import java.util.List; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; @@ -181,24 +180,5 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // This may or may not duplicate the above final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); - - // The following catches a few more jars for org.apache.tomee.catalina.TomEEWebappClassLoader: - // Type WebResourceRoot - final Object resourcesField = ReflectionUtils.getFieldVal(classLoader, "resources", false); - if (resourcesField != null) { - // Type List - final Object jars = ReflectionUtils.getFieldVal(resourcesField, "jarResources", false); - if (jars != null && jars instanceof Iterable) { - final Iterable it = (Iterable) jars; - // Type Iterator - final Iterator jarIt = it.iterator(); - while (jarIt.hasNext()) { - // Type WebResourceSet - final Object set = jarIt.next(); - final Object baseUrl = ReflectionUtils.invokeMethod(set, "getBaseUrl", false); - classpathOrder.addClasspathEntry(baseUrl, classLoader, scanSpec, log); - } - } - } } } From 56c96cf560990043682043bb23132459de3f872c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:02:43 -0600 Subject: [PATCH 026/713] Update comments --- .../classloaderhandler/CxfContainerClassLoaderHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java index 186877c70..398f8f5da 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -73,8 +73,10 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla } catch (LinkageError | ClassNotFoundException e) { // Ignore } + // tccl = TomcatClassLoader classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(classLoader, "tccl", false), /* isParent = */ false, log); + // No need to call classLoaderOrder.add(classLoader, log) because this ClassLoader doesn't load any classes } /** @@ -91,6 +93,6 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - // Classloader doesn't do any classloading of its own, it only delegates to one or two other classloaders + // Classloader doesn't do any classloading of its own, it only delegates to other classloaders } } From 5028f4551a70a93bf508ce43d21ed5da0abf5b6a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:03:10 -0600 Subject: [PATCH 027/713] Add entry to ClassLoaderHandlerRegistry (#515) --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java index 7cb427b85..e9f946f75 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -58,6 +58,7 @@ public class ClassLoaderHandlerRegistry { new ClassLoaderHandlerRegistryEntry(OSGiDefaultClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(SpringBootRestartClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(TomcatWebappClassLoaderBaseHandler.class), + new ClassLoaderHandlerRegistryEntry(CxfContainerClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(PlexusClassWorldsClassRealmClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(QuarkusClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(UnoOneJarClassLoaderHandler.class), From d3ffcb872a307012d012407a53d4a0f0254d483d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:53:11 -0600 Subject: [PATCH 028/713] Source > Format --- .../TomcatWebappClassLoaderBaseHandler.java | 281 +++++++++--------- 1 file changed, 143 insertions(+), 138 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index d93f5fa1c..a8fcd8af0 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -39,146 +39,151 @@ /** Extract classpath entries from the Tomcat/Catalina WebappClassLoaderBase. */ class TomcatWebappClassLoaderBaseHandler implements ClassLoaderHandler { - /** Class cannot be constructed. */ - private TomcatWebappClassLoaderBaseHandler() { - } + /** Class cannot be constructed. */ + private TomcatWebappClassLoaderBaseHandler() { + } - /** - * Check whether this {@link ClassLoaderHandler} can handle a given - * {@link ClassLoader}. - * - * @param classLoaderClass the {@link ClassLoader} class or one of its - * superclasses. - * @param log the log - * @return true if this {@link ClassLoaderHandler} can handle the - * {@link ClassLoader}. - */ - public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); - } + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. + * @param log + * the log + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. + */ + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); + } - /** - * Return true if this classloader delegates to its parent. - * - * @param classLoader the {@link ClassLoader}. - * @return true if this classloader delegates to its parent. - */ - private static boolean isParentFirst(final ClassLoader classLoader) { - final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); - if (delegateObject != null) { - return (boolean) delegateObject; - } - // Assume parent-first delegation order - return true; - } + /** + * Return true if this classloader delegates to its parent. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this classloader delegates to its parent. + */ + private static boolean isParentFirst(final ClassLoader classLoader) { + final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); + if (delegateObject != null) { + return (boolean) delegateObject; + } + // Assume parent-first delegation order + return true; + } - /** - * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. - * - * @param classLoader the {@link ClassLoader} to find the order for. - * @param classLoaderOrder a {@link ClassLoaderOrder} object to update. - * @param log the log - */ - public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, - final LogNode log) { - final boolean isParentFirst = isParentFirst(classLoader); - if (isParentFirst) { - // Use parent-first delegation order - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - } - if ("org.apache.tomee.catalina.TomEEWebappClassLoader".equals(classLoader.getClass().getName())) { - // TomEEWebappClassLoader has a lot of complex delegation rules, including classname-specific - // delegation, which is not supported by the current ClassGraph model, so we just try to approximate - // the delegation order with a fixed order. - try { - classLoaderOrder.delegateTo(Class.forName("org.apache.openejb.OpenEJB").getClassLoader(), - /* isParent = */ true, log); - } catch (LinkageError | ClassNotFoundException e) { - // Ignore - } - } - classLoaderOrder.add(classLoader, log); - if (!isParentFirst) { - // Use parent-last delegation order - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - } - } + /** + * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the order for. + * @param classLoaderOrder + * a {@link ClassLoaderOrder} object to update. + * @param log + * the log + */ + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + final boolean isParentFirst = isParentFirst(classLoader); + if (isParentFirst) { + // Use parent-first delegation order + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + } + if ("org.apache.tomee.catalina.TomEEWebappClassLoader".equals(classLoader.getClass().getName())) { + // TomEEWebappClassLoader has a lot of complex delegation rules, including classname-specific + // delegation, which is not supported by the current ClassGraph model, so we just try to approximate + // the delegation order with a fixed order. + try { + classLoaderOrder.delegateTo(Class.forName("org.apache.openejb.OpenEJB").getClassLoader(), + /* isParent = */ true, log); + } catch (LinkageError | ClassNotFoundException e) { + // Ignore + } + } + classLoaderOrder.add(classLoader, log); + if (!isParentFirst) { + // Use parent-last delegation order + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + } + } - /** - * Find the classpath entries for the associated {@link ClassLoader}. - * - * @param classLoader the {@link ClassLoader} to find the classpath entries - * order for. - * @param classpathOrder a {@link ClasspathOrder} object to update. - * @param scanSpec the {@link ScanSpec}. - * @param log the log. - */ - public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, - final ScanSpec scanSpec, final LogNode log) { - // type StandardRoot (implements WebResourceRoot) - final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); - // type List - final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); - classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); - // type List> - // members: preResources, mainResources, classResources, jarResources, - // postResources - @SuppressWarnings("unchecked") - final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", - false); - if (allResources != null) { - // type List - for (final List webResourceSetList : allResources) { - // type WebResourceSet - // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, - // EmptyResourceSet} - for (final Object webResourceSet : webResourceSetList) { - if (webResourceSet != null) { - // For DirResourceSet - final File file = (File) ReflectionUtils.invokeMethod(webResourceSet, "getFileBase", false); - String base = file == null ? null : file.getPath(); - if (base == null) { - // For FileResourceSet - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBase", false); - } - if (base == null) { - // For JarResourceSet and JarWarResourceSet - // The absolute path to the WAR file on the file system in which the JAR is - // located - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBaseUrlString", false); - } - if (base != null) { - // For JarWarResourceSet: the path within the WAR file where the JAR file is - // located - final String archivePath = (String) ReflectionUtils.getFieldVal(webResourceSet, - "archivePath", false); - if (archivePath != null && !archivePath.isEmpty()) { - // If archivePath is non-null, this is a jar within a war - base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); - } - final String className = webResourceSet.getClass().getName(); - final boolean isJar = className - .equals("java.org.apache.catalina.webresources.JarResourceSet") - || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); - // The path within this WebResourceSet where resources will be served from, - // e.g. for a resource JAR, this would be "META-INF/resources" - final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, - "getInternalPath", false); - if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { - classpathOrder.addClasspathEntryObject( - base + (isJar ? "!" : "") - + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), - classLoader, scanSpec, log); - } else { - classpathOrder.addClasspathEntryObject(base, classLoader, scanSpec, log); - } - } - } - } - } - } - // This may or may not duplicate the above - final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); - classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); - } + /** + * Find the classpath entries for the associated {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader} to find the classpath entries order for. + * @param classpathOrder + * a {@link ClasspathOrder} object to update. + * @param scanSpec + * the {@link ScanSpec}. + * @param log + * the log. + */ + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // type StandardRoot (implements WebResourceRoot) + final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); + // type List + final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); + classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); + // type List> + // members: preResources, mainResources, classResources, jarResources, + // postResources + @SuppressWarnings("unchecked") + final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", + false); + if (allResources != null) { + // type List + for (final List webResourceSetList : allResources) { + // type WebResourceSet + // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, + // EmptyResourceSet} + for (final Object webResourceSet : webResourceSetList) { + if (webResourceSet != null) { + // For DirResourceSet + final File file = (File) ReflectionUtils.invokeMethod(webResourceSet, "getFileBase", false); + String base = file == null ? null : file.getPath(); + if (base == null) { + // For FileResourceSet + base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBase", false); + } + if (base == null) { + // For JarResourceSet and JarWarResourceSet + // The absolute path to the WAR file on the file system in which the JAR is + // located + base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBaseUrlString", false); + } + if (base != null) { + // For JarWarResourceSet: the path within the WAR file where the JAR file is + // located + final String archivePath = (String) ReflectionUtils.getFieldVal(webResourceSet, + "archivePath", false); + if (archivePath != null && !archivePath.isEmpty()) { + // If archivePath is non-null, this is a jar within a war + base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); + } + final String className = webResourceSet.getClass().getName(); + final boolean isJar = className + .equals("java.org.apache.catalina.webresources.JarResourceSet") + || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); + // The path within this WebResourceSet where resources will be served from, + // e.g. for a resource JAR, this would be "META-INF/resources" + final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, + "getInternalPath", false); + if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { + classpathOrder.addClasspathEntryObject(base + (isJar ? "!" : "") + + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), + classLoader, scanSpec, log); + } else { + classpathOrder.addClasspathEntryObject(base, classLoader, scanSpec, log); + } + } + } + } + } + } + // This may or may not duplicate the above + final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); + classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); + } } From c35194970f8cb25fd5775dd172bed151d7abe5ac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:53:26 -0600 Subject: [PATCH 029/713] Code > Cleanup --- .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 048ae1a30..7cf6a3cbc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -103,7 +103,7 @@ public static String currDirPath() { // user.dir should be the current directory at the time the JVM is started, which is // where classpath elements should be resolved relative to Path path = null; - String currDirPathStr = System.getProperty("user.dir"); + final String currDirPathStr = System.getProperty("user.dir"); if (currDirPathStr != null) { try { path = Paths.get(currDirPathStr); @@ -120,7 +120,7 @@ public static String currDirPath() { // Fall through } } - + // Normalize current directory the same way all other paths are normalized in ClassGraph, // for consistency currDirPath = FastPathResolver.resolve(path == null ? "" : path.toString()); From abe57d4b1d0bcf9fdaa9e054bb78349eadab3ac8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:53:36 -0600 Subject: [PATCH 030/713] Add comment --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java index e9f946f75..5da03c36c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -74,7 +74,10 @@ public class ClassLoaderHandlerRegistry { new ClassLoaderHandlerRegistryEntry(URLClassLoaderHandler.class), // Placeholder for delegation to a ClassGraphClassLoader instance from an outer nested scan - new ClassLoaderHandlerRegistryEntry(ClassGraphClassLoaderHandler.class))); + new ClassLoaderHandlerRegistryEntry(ClassGraphClassLoaderHandler.class) + + // FallbackClassLoaderHandler.class is registered separately below + )); /** Fallback ClassLoaderHandler. */ public static final ClassLoaderHandlerRegistryEntry FALLBACK_HANDLER = new ClassLoaderHandlerRegistryEntry( From 66289293103da132d01c63ecb647792b8be0e40f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:54:03 -0600 Subject: [PATCH 031/713] Use IdentityHashMap for all maps and sets (#515) --- .../classpath/ClassLoaderOrder.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java index 520d57d70..a39f70212 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -30,8 +30,8 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -52,23 +52,28 @@ public class ClassLoaderOrder { * The set of all {@link ClassLoader} instances that have been added to the order so far, so that classloaders * don't get added twice. */ - private final Set added = new HashSet<>(); + // Need to use IdentityHashMap for maps and sets here, because TomEE weirdly makes instances of + // CxfContainerClassLoader equal to (via .equals()) the instance of TomEEWebappClassLoader that it + // delegates to (#515) + private final Set added = Collections.newSetFromMap(new IdentityHashMap()); /** * The set of all {@link ClassLoader} instances that have been delegated to so far, to prevent an infinite loop * in delegation. */ - private final Set delegatedTo = new HashSet<>(); + private final Set delegatedTo = Collections + .newSetFromMap(new IdentityHashMap()); /** * The set of all parent {@link ClassLoader} instances that have been delegated to so far, to enable * {@link ClassGraph#ignoreParentClassLoaders()}. */ - private final Set allParentClassLoaders = new HashSet<>(); + private final Set allParentClassLoaders = Collections + .newSetFromMap(new IdentityHashMap()); /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ private final Map classLoaderToClassLoaderHandlerRegistryEntry = // - new HashMap<>(); + new IdentityHashMap(); // ------------------------------------------------------------------------------------------------------------- @@ -130,7 +135,7 @@ private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader class } /** - * Add a {@link ClassLoader} to the {@link ClassLoader} order at the current position. + * Add a {@link ClassLoader} to the ClassLoader order at the current position. * * @param classLoader * the class loader From e8728acac309ad078c8e2a25070f6a46b2efa954 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:54:20 -0600 Subject: [PATCH 032/713] Use IdentityHashMap for all maps and sets (#515) --- .../io/github/classgraph/classpath/ClasspathFinder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index aa74ba9cb..964214e00 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -259,8 +259,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Otherwise add classpath entries to classpathOrder, and add the classloader to the // final classloader ordering final LogNode classloaderHandlerLog = classloaderURLLog == null ? null - : classloaderURLLog.log("Classloader " + classLoader + " is handled by " - + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); + : classloaderURLLog + .log("Classloader " + classLoader.getClass().getName() + " is handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, classloaderHandlerLog); finalClassLoaderOrder.add(classLoader); From 4744411249988d6311a4f750a8a09b903240e970 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:54:37 -0600 Subject: [PATCH 033/713] Improve logging --- .../classloaderhandler/CxfContainerClassLoaderHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java index 398f8f5da..e9c4ba372 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -76,7 +76,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // tccl = TomcatClassLoader classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(classLoader, "tccl", false), /* isParent = */ false, log); - // No need to call classLoaderOrder.add(classLoader, log) because this ClassLoader doesn't load any classes + // This classloader doesn't actually load any classes, but add it to the order to improve logging + classLoaderOrder.add(classLoader, log); } /** From 5f78024aec4b9fcf789e982c88f3884790a9c226 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:58:53 -0600 Subject: [PATCH 034/713] [maven-release-plugin] prepare release classgraph-4.8.107 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0fbdd1e40..0f2ec3642 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.107-SNAPSHOT + 4.8.107 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.107 From c5bc25337bcee46b87b8ea9ae8e1126bcffce678 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Jun 2021 03:58:57 -0600 Subject: [PATCH 035/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0f2ec3642..48606d06d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.107 + 4.8.108-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.107 + classgraph-4.8.105 From baf88b8023c471c5c71ca57bdf33ad47db9deebf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Jun 2021 17:45:55 -0600 Subject: [PATCH 036/713] Add constant pool tag type 17 (#527) --- src/main/java/io/github/classgraph/Classfile.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7d0f6c60b..b9a774a16 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -928,7 +928,7 @@ private Object getFieldConstantPoolValue(final int tag, final char fieldTypeDesc default: // ClassGraph doesn't expect other types // (N.B. in particular, enum values are not stored in the constant pool, so don't need to be handled) - throw new ClassfileFormatException("Unknown constant pool tag " + tag + ", " + throw new ClassfileFormatException("Unknown field constant pool tag " + tag + ", " + "cannot continue reading class. Please report this at " + "https://github.com/classgraph/classgraph/issues"); } @@ -1097,13 +1097,14 @@ private void readConstantPoolEntries() throws IOException { entryOffset[i] = reader.currPos(); switch (entryTag[i]) { case 0: // Impossible, probably buffer underflow - throw new ClassfileFormatException("Unknown constant pool tag 0 in classfile " + relativePath + throw new ClassfileFormatException("Invalid constant pool tag 0 in classfile " + relativePath + " (possible buffer underflow issue). Please report this at " + "https://github.com/classgraph/classgraph/issues"); case 1: // Modified UTF8 final int strLen = reader.readUnsignedShort(); reader.skip(strLen); break; + // There is no constant pool tag type 2 case 3: // int, short, char, byte, boolean are all represented by Constant_INTEGER case 4: // float reader.skip(4); @@ -1145,12 +1146,16 @@ private void readConstantPoolEntries() throws IOException { } indirectStringRefs[i] = (nameRef << 16) | typeRef; break; + // There is no constant pool tag type 13 or 14 case 15: // method handle reader.skip(3); break; case 16: // method type reader.skip(2); break; + case 17: // dynamic + reader.skip(4); + break; case 18: // invoke dynamic reader.skip(4); break; From 82c6aad90300caa7f773ea9ede1d84e3b15ec776 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Jun 2021 17:46:30 -0600 Subject: [PATCH 037/713] [maven-release-plugin] prepare release classgraph-4.8.108 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48606d06d..6e9c589ed 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.108-SNAPSHOT + 4.8.108 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.108 From f5aa8909519fe4e15eb61f039e61438ca5e2a3cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Jun 2021 17:46:35 -0600 Subject: [PATCH 038/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6e9c589ed..b9337b6ba 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.108 + 4.8.109-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.108 + classgraph-4.8.105 From 4a6d1eb664adc35b902b80a68da32fb54718a2f3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Jun 2021 18:05:35 -0600 Subject: [PATCH 039/713] Updated Javadoc --- src/main/java/io/github/classgraph/Classfile.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index b9a774a16..9efc0dac0 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -54,6 +54,9 @@ * A classfile binary format parser. Implements its own buffering to avoid the overhead of using DataInputStream. * This class should only be used by a single thread at a time, but can be re-used to scan multiple classfiles in * sequence, to avoid re-allocating buffer memory. + * + *

+ * See the class file format spec. */ class Classfile { /** The {@link ClassfileReader} for the current classfile. */ @@ -1104,7 +1107,7 @@ private void readConstantPoolEntries() throws IOException { final int strLen = reader.readUnsignedShort(); reader.skip(strLen); break; - // There is no constant pool tag type 2 + // There is no constant pool tag type 2 case 3: // int, short, char, byte, boolean are all represented by Constant_INTEGER case 4: // float reader.skip(4); @@ -1146,7 +1149,7 @@ private void readConstantPoolEntries() throws IOException { } indirectStringRefs[i] = (nameRef << 16) | typeRef; break; - // There is no constant pool tag type 13 or 14 + // There is no constant pool tag type 13 or 14 case 15: // method handle reader.skip(3); break; From fb2419394b2258fc894f84e48559b8ab6f3b589b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Jun 2021 02:00:54 +0000 Subject: [PATCH 040/713] Bump assertj-core from 3.19.0 to 3.20.0 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.19.0 to 3.20.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.19.0...assertj-core-3.20.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b9337b6ba..ae394b7cc 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.19.0 + 3.20.0 test From 90c9ec50f6b08d3ec6460a8768b37b06eabf6cca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Jun 2021 02:01:37 +0000 Subject: [PATCH 041/713] Bump assertj-core from 3.20.0 to 3.20.1 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.0 to 3.20.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.0...assertj-core-3.20.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae394b7cc..40fe63672 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.20.0 + 3.20.1 test From 769a50b68def2d16009fe70f83ce983e1ba615f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 02:00:53 +0000 Subject: [PATCH 042/713] Bump assertj-core from 3.20.1 to 3.20.2 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.1 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.1...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40fe63672..99f0b2f5f 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.20.1 + 3.20.2 test From b8cc7e92edd72f7d2da77595f669c7d485b24eb7 Mon Sep 17 00:00:00 2001 From: itmrat01 Date: Thu, 1 Jul 2021 11:23:09 +0200 Subject: [PATCH 043/713] [ICO-XXX] Add class loader handler for quarkus 1.13.3 --- pom.xml | 2 +- .../QuarkusClassLoaderHandler.java | 30 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 99f0b2f5f..71f34746a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.109-SNAPSHOT + 4.8.109-itm-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index a057642eb..01e8ac326 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -28,15 +28,16 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.nio.file.Path; -import java.util.Collection; - import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; + /** * Extract classpath entries from the Quarkus ClassLoader. */ @@ -47,6 +48,9 @@ class QuarkusClassLoaderHandler implements ClassLoaderHandler { // Classloader since Quarkus 1.3 private static final String QUARKUS_CLASSLOADER = "io.quarkus.bootstrap.classloading.QuarkusClassLoader"; + // Classloader since Quarkus 1.13 + private static final String RUNNER_CLASSLOADER = "io.quarkus.bootstrap.runner.RunnerClassLoader"; + /** * Class cannot be constructed. */ @@ -64,7 +68,8 @@ private QuarkusClassLoaderHandler() { */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return RUNTIME_CLASSLOADER.equals(classLoaderClass.getName()) - || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName()); + || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName()) + || RUNNER_CLASSLOADER.equals(classLoaderClass.getName()); } /** @@ -103,6 +108,8 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class findClasspathOrderForRuntimeClassloader(classLoader, classpathOrder, scanSpec, log); } else if (QUARKUS_CLASSLOADER.equals(classLoaderName)) { findClasspathOrderForQuarkusClassloader(classLoader, classpathOrder, scanSpec, log); + } else if (RUNNER_CLASSLOADER.equals(classLoaderName)) { + findClasspathOrderForRunnerClassloader(classLoader, classpathOrder, scanSpec, log); } } @@ -133,4 +140,19 @@ private static void findClasspathOrderForRuntimeClassloader(final ClassLoader cl } } } + + @SuppressWarnings("unchecked") + private static void findClasspathOrderForRunnerClassloader(final ClassLoader classLoader, + final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { + for (final Object[] elementArray : ((Map) ReflectionUtils.getFieldVal(classLoader, + "resourceDirectoryMap", false)).values()) { + for (Object element : elementArray) { + final String elementClassName = element.getClass().getName(); + if ("io.quarkus.bootstrap.runner.JarResource".equals(elementClassName)) { + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "jarPath", false), + classLoader, scanSpec, log); + } + } + } + } } From 05988cab16e96795cf8cb774cb18e71508ed6ab9 Mon Sep 17 00:00:00 2001 From: itmrat01 Date: Thu, 1 Jul 2021 12:36:42 +0200 Subject: [PATCH 044/713] [ICO-XXX] New version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71f34746a..99ebd0642 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.109-itm-SNAPSHOT + 4.8.108-itm ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 5b34cf630b8131b2658e7408433d3038bff605d2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 1 Jul 2021 05:42:48 -0600 Subject: [PATCH 045/713] Fix version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 99ebd0642..99f0b2f5f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.108-itm + 4.8.109-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 59507683d5d9b1f3e357991e81b8ed8ec7d71efa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 1 Jul 2021 05:43:37 -0600 Subject: [PATCH 046/713] Source > Cleanup --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 10 +++++----- .../nonapi/io/github/classgraph/scanspec/ScanSpec.java | 6 +----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index 01e8ac326..13317a2ff 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -28,16 +28,16 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; + import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; -import java.nio.file.Path; -import java.util.Collection; -import java.util.Map; - /** * Extract classpath entries from the Quarkus ClassLoader. */ @@ -146,7 +146,7 @@ private static void findClasspathOrderForRunnerClassloader(final ClassLoader cla final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { for (final Object[] elementArray : ((Map) ReflectionUtils.getFieldVal(classLoader, "resourceDirectoryMap", false)).values()) { - for (Object element : elementArray) { + for (final Object element : elementArray) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.runner.JarResource".equals(elementClassName)) { classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "jarPath", false), diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java index ae7ad0e70..a29f34569 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -476,11 +476,7 @@ public enum ScanSpecPathMatch { */ public ScanSpecPathMatch dirAcceptMatchStatus(final String relativePath) { // In rejected path - if (pathAcceptReject.isRejected(relativePath)) { - // The directory is rejected. - return ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX; - } - if (pathPrefixAcceptReject.isRejected(relativePath)) { + if (pathAcceptReject.isRejected(relativePath) || pathPrefixAcceptReject.isRejected(relativePath)) { // An prefix of this path is rejected. return ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX; } From 94de3b44f88b8cba9899bb5ae13d33681d5cdbd6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 1 Jul 2021 05:45:35 -0600 Subject: [PATCH 047/713] [maven-release-plugin] prepare release classgraph-4.8.109 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 99f0b2f5f..e6a29f51e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.109-SNAPSHOT + 4.8.109 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.109 From 8745bea62e6998739982b65cd1fee6ca0de1004e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 1 Jul 2021 05:45:41 -0600 Subject: [PATCH 048/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e6a29f51e..179b4a388 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.109 + 4.8.110-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.109 + classgraph-4.8.105 From 6ed47b0271b9067f97249d4ef83bd702a13f6cdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 02:00:50 +0000 Subject: [PATCH 049/713] Bump slf4j-jdk14 from 2.0.0-alpha1 to 2.0.0-alpha2 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha1 to 2.0.0-alpha2. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 179b4a388..744486b63 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha1 + 2.0.0-alpha2 test From 0314cd18298e4c13ec84148ad2fb336b10de42d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 02:00:52 +0000 Subject: [PATCH 050/713] Bump slf4j-api from 2.0.0-alpha1 to 2.0.0-alpha2 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha1 to 2.0.0-alpha2. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 179b4a388..08b7b89e4 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.slf4j slf4j-api - 2.0.0-alpha1 + 2.0.0-alpha2 test From fad9f4b392d62ecf9b5e65b398cb29190919458e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 6 Jul 2021 01:34:54 -0600 Subject: [PATCH 051/713] Add getParameterValues(boolean includeDefaultValues) (#535) --- .../io/github/classgraph/AnnotationInfo.java | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index a619edaa2..eb8058211 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -114,24 +114,28 @@ public AnnotationParameterValueList getDefaultParameterValues() { /** * Get the parameter values. * + * @param includeDefaultValues + * if true, include default values for any annotation parameter value that is missing. * @return The parameter values of this annotation, including any default parameter values inherited from the - * annotation class definition, or the empty list if none. + * annotation class definition (if requested), or the empty list if none. */ - public AnnotationParameterValueList getParameterValues() { + public AnnotationParameterValueList getParameterValues(boolean includeDefaultValues) { + final ClassInfo classInfo = getClassInfo(); + if (classInfo == null) { + // ClassInfo has not yet been set, just return values without defaults + // (happens when trying to log AnnotationInfo during scanning, before ScanResult is available) + return annotationParamValues == null ? AnnotationParameterValueList.EMPTY_LIST : annotationParamValues; + } + // Lazily convert any Object[] arrays of boxed types to primitive arrays + if (annotationParamValues != null && !annotationParamValuesHasBeenConvertedToPrimitive) { + annotationParamValues.convertWrapperArraysToPrimitiveArrays(classInfo); + annotationParamValuesHasBeenConvertedToPrimitive = true; + } + if (!includeDefaultValues) { + // Don't include defaults + return annotationParamValues == null ? AnnotationParameterValueList.EMPTY_LIST : annotationParamValues; + } if (annotationParamValuesWithDefaults == null) { - final ClassInfo classInfo = getClassInfo(); - if (classInfo == null) { - // ClassInfo has not yet been set, just return values without defaults - // (happens when trying to log AnnotationInfo during scanning, before ScanResult is available) - return annotationParamValues == null ? AnnotationParameterValueList.EMPTY_LIST - : annotationParamValues; - } - - // Lazily convert any Object[] arrays of boxed types to primitive arrays - if (annotationParamValues != null && !annotationParamValuesHasBeenConvertedToPrimitive) { - annotationParamValues.convertWrapperArraysToPrimitiveArrays(classInfo); - annotationParamValuesHasBeenConvertedToPrimitive = true; - } if (classInfo.annotationDefaultParamValues != null && !classInfo.annotationDefaultParamValuesHasBeenConvertedToPrimitive) { classInfo.annotationDefaultParamValues.convertWrapperArraysToPrimitiveArrays(classInfo); @@ -193,6 +197,16 @@ public AnnotationParameterValueList getParameterValues() { return annotationParamValuesWithDefaults; } + /** + * Get the parameter values. + * + * @return The parameter values of this annotation, including any default parameter values inherited from the + * annotation class definition, or the empty list if none. + */ + public AnnotationParameterValueList getParameterValues() { + return getParameterValues(true); + } + // ------------------------------------------------------------------------------------------------------------- /** From a8dfee3159b0a5244581a561ac99695e2c559fa8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 7 Jul 2021 00:37:11 -0600 Subject: [PATCH 052/713] [maven-release-plugin] prepare release classgraph-4.8.110 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5147ac3c5..d77238e1e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.110-SNAPSHOT + 4.8.110 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.110 From 4e347fa2718f23d87425228f76a750dbd60a1dde Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 7 Jul 2021 00:37:16 -0600 Subject: [PATCH 053/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d77238e1e..08a49aa15 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.110 + 4.8.111-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.110 + classgraph-4.8.105 From e485587904034507917aea594c1159125d4186c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 02:01:06 +0000 Subject: [PATCH 054/713] Bump maven-enforcer-plugin from 3.0.0-M3 to 3.0.0 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.0.0-M3 to 3.0.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0-M3...enforcer-3.0.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 08a49aa15..d5c322c17 100644 --- a/pom.xml +++ b/pom.xml @@ -149,7 +149,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.0.0 org.apache.maven.plugins From 54dadaf0f936099c09f6a542254624bffabdcaba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 2 Aug 2021 18:13:44 -0600 Subject: [PATCH 055/713] Allow globs in `acceptClasses` and `rejectClasses` (#536) --- src/main/java/io/github/classgraph/ClassGraph.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index b712ab5eb..2f4fade9f 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -913,16 +913,13 @@ public ClassGraph blacklistPaths(final String... paths) { * * * @param classNames - * The fully-qualified names of classes to scan (using '.' as a separator). May not include a glob - * wildcard ({@code '*'}). + * The fully-qualified names of classes to scan (using '.' as a separator). To match a class name by + * suffix in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. * @return this (for method chaining). */ public ClassGraph acceptClasses(final String... classNames) { enableClassInfo(); for (final String className : classNames) { - if (className.contains("*")) { - throw new IllegalArgumentException("Cannot use a glob wildcard here: " + className); - } final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className); // Accept the class itself scanSpec.classAcceptReject.addToAccept(classNameNormalized); @@ -959,16 +956,13 @@ public ClassGraph whitelistClasses(final String... classNames) { * N.B. Automatically calls {@link #enableClassInfo()}. * * @param classNames - * The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob - * wildcard ({@code '*'}). + * The fully-qualified names of classes to reject (using '.' as a separator). To match a class name + * by suffix in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. * @return this (for method chaining). */ public ClassGraph rejectClasses(final String... classNames) { enableClassInfo(); for (final String className : classNames) { - if (className.contains("*")) { - throw new IllegalArgumentException("Cannot use a glob wildcard here: " + className); - } final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className); scanSpec.classAcceptReject.addToReject(classNameNormalized); scanSpec.classfilePathAcceptReject From 2022d94b055b55c04fac4a163523325c62636d9a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 2 Aug 2021 18:17:34 -0600 Subject: [PATCH 056/713] Update JavaDoc --- src/main/java/io/github/classgraph/ClassGraph.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 2f4fade9f..83d00c885 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -914,7 +914,7 @@ public ClassGraph blacklistPaths(final String... paths) { * * @param classNames * The fully-qualified names of classes to scan (using '.' as a separator). To match a class name by - * suffix in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. + * glob in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. * @return this (for method chaining). */ public ClassGraph acceptClasses(final String... classNames) { @@ -938,8 +938,7 @@ public ClassGraph acceptClasses(final String... classNames) { * Use {@link #acceptClasses(String...)} instead. * * @param classNames - * The fully-qualified names of classes to scan (using '.' as a separator). May not include a glob - * wildcard ({@code '*'}). + * The fully-qualified names of classes to scan (using '.' as a separator). * @return this (for method chaining). * @deprecated Use {@link #acceptClasses(String...)} instead. */ @@ -957,7 +956,7 @@ public ClassGraph whitelistClasses(final String... classNames) { * * @param classNames * The fully-qualified names of classes to reject (using '.' as a separator). To match a class name - * by suffix in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. + * by glob in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}. * @return this (for method chaining). */ public ClassGraph rejectClasses(final String... classNames) { @@ -975,8 +974,7 @@ public ClassGraph rejectClasses(final String... classNames) { * Use {@link #rejectClasses(String...)} instead. * * @param classNames - * The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob - * wildcard ({@code '*'}). + * The fully-qualified names of classes to reject (using '.' as a separator). * @return this (for method chaining). * @deprecated Use {@link #rejectClasses(String...)} instead. */ From 4aeda588fc15528c21fee04618a74296f36b7d2a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 3 Aug 2021 23:09:17 -0600 Subject: [PATCH 057/713] [maven-release-plugin] prepare release classgraph-4.8.111 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d5c322c17..24d2428ab 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.111-SNAPSHOT + 4.8.111 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.111 From 71ba4a2bd8424490bb911149985d2d0ebb930fbc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 3 Aug 2021 23:09:20 -0600 Subject: [PATCH 058/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 24d2428ab..72b6c1f18 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.111 + 4.8.112-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.111 + classgraph-4.8.105 From 681362ad6b0b9d9abaffb2e07099ce54d7a41fa3 Mon Sep 17 00:00:00 2001 From: Kshitiz Garg Date: Thu, 5 Aug 2021 14:05:06 +0530 Subject: [PATCH 059/713] Adding SecureDocumentBuilderFactory & SecureXPATHFactory to prevent XXE( XML External Entity) attack --- .../classgraph/utils/VersionFinder.java | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java index cef6e6959..848c8199b 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -39,10 +39,13 @@ import java.util.Locale; import java.util.Properties; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathFactoryConfigurationException; import org.w3c.dom.Document; import io.github.classgraph.ClassGraph; @@ -221,9 +224,9 @@ public static synchronized String getVersion() { for (int i = 0; i < 3 && path != null; i++, path = path.getParent()) { final Path pom = path.resolve("pom.xml"); try (InputStream is = Files.newInputStream(pom)) { - final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); + final Document doc = getSecureDocumentBuilderFactory().newDocumentBuilder().parse(is); doc.getDocumentElement().normalize(); - String version = (String) XPathFactory.newInstance().newXPath().compile("/project/version") + String version = (String) getSecureXPathFactory().newXPath().compile("/project/version") .evaluate(doc, XPathConstants.STRING); if (version != null) { version = version.trim(); @@ -276,4 +279,40 @@ public static synchronized String getVersion() { } return "unknown"; } + + /** + * Helper method to provide a XXE secured DocumentBuilder Factory. + * + * reference - https://gist.github.com/AlainODea/1779a7c6a26a5c135280bc9b3b71868f + * reference - https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + * @return DocumentBuilderFactory + * @throws ParserConfigurationException + */ + private static DocumentBuilderFactory getSecureDocumentBuilderFactory() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setXIncludeAware(false); + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + dbf.setExpandEntityReferences(false); + dbf.setNamespaceAware(true); + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + return dbf; + } + + /** + * Helper method to provide a XXE secured XPathFactory Factory. + * + * reference - reference - https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + * @return XPathFactory + * @throws XPathFactoryConfigurationException + */ + private static XPathFactory getSecureXPathFactory() throws XPathFactoryConfigurationException { + XPathFactory xPathFactory = XPathFactory.newInstance(); + xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + return xPathFactory; + } } From 5d8d61d7a86338920b07e2c6241063040b408fa2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 5 Aug 2021 10:50:33 -0600 Subject: [PATCH 060/713] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f2293fffb..c775e14d9 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,7 @@ Some other classpath scanning mechanisms include: * [org.clapper.classutil.ClassFinder](https://github.com/bmc/classutil/blob/master/src/main/scala/org/clapper/classutil/ClassFinder.scala) * [com.google.common.reflect.ClassPath](https://github.com/google/guava/blob/master/guava/src/com/google/common/reflect/ClassPath.java) * [jdependency](https://github.com/tcurdt/jdependency) +* [BurningWave Core](https://github.com/burningwave/core#burningwave-core-) ## License From 2a5dbf7f48d135e6b675e829b8a3da26c0ad9382 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 5 Aug 2021 10:50:49 -0600 Subject: [PATCH 061/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c775e14d9..8a35c4d44 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ Some other classpath scanning mechanisms include: * [org.clapper.classutil.ClassFinder](https://github.com/bmc/classutil/blob/master/src/main/scala/org/clapper/classutil/ClassFinder.scala) * [com.google.common.reflect.ClassPath](https://github.com/google/guava/blob/master/guava/src/com/google/common/reflect/ClassPath.java) * [jdependency](https://github.com/tcurdt/jdependency) -* [BurningWave Core](https://github.com/burningwave/core#burningwave-core-) +* [Burningwave Core](https://github.com/burningwave/core#burningwave-core-) ## License From 5e32b914c4a0a579a5351b3eaa0e7b0d45612261 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 5 Aug 2021 11:46:11 -0600 Subject: [PATCH 062/713] Source > Cleanup --- .../io/github/classgraph/AnnotationInfo.java | 2 +- .../io/github/classgraph/scanspec/ScanSpec.java | 17 +++++++---------- .../github/classgraph/utils/VersionFinder.java | 11 +++++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index eb8058211..7dbcba342 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -119,7 +119,7 @@ public AnnotationParameterValueList getDefaultParameterValues() { * @return The parameter values of this annotation, including any default parameter values inherited from the * annotation class definition (if requested), or the empty list if none. */ - public AnnotationParameterValueList getParameterValues(boolean includeDefaultValues) { + public AnnotationParameterValueList getParameterValues(final boolean includeDefaultValues) { final ClassInfo classInfo = getClassInfo(); if (classInfo == null) { // ClassInfo has not yet been set, just return values without defaults diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java index a29f34569..0c0b802ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -504,16 +504,13 @@ public ScanSpecPathMatch dirAcceptMatchStatus(final String relativePath) { } // Ancestor of accepted path - if (relativePath.equals("/")) { - // The default package is always the ancestor of accepted paths (need to keep recursing) - return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; - } - if (pathAcceptReject.acceptHasPrefix(relativePath)) { - // relativePath is an ancestor (prefix) of an accepted path - return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; - } - if (classfilePathAcceptReject.acceptHasPrefix(relativePath)) { - // relativePath is an ancestor (prefix) of an accepted class' parent directory + if ( + // The default package is always the ancestor of accepted paths (need to keep recursing) + relativePath.equals("/") + // relativePath is an ancestor (prefix) of an accepted path + || pathAcceptReject.acceptHasPrefix(relativePath) + // relativePath is an ancestor (prefix) of an accepted class' parent directory + || classfilePathAcceptReject.acceptHasPrefix(relativePath)) { return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java index 848c8199b..0fc43914d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -44,8 +44,8 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; - import javax.xml.xpath.XPathFactoryConfigurationException; + import org.w3c.dom.Document; import io.github.classgraph.ClassGraph; @@ -284,12 +284,14 @@ public static synchronized String getVersion() { * Helper method to provide a XXE secured DocumentBuilder Factory. * * reference - https://gist.github.com/AlainODea/1779a7c6a26a5c135280bc9b3b71868f + * * reference - https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + * * @return DocumentBuilderFactory * @throws ParserConfigurationException */ private static DocumentBuilderFactory getSecureDocumentBuilderFactory() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setXIncludeAware(false); dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); @@ -306,12 +308,13 @@ private static DocumentBuilderFactory getSecureDocumentBuilderFactory() throws P /** * Helper method to provide a XXE secured XPathFactory Factory. * - * reference - reference - https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + * reference - https://rules.sonarsource.com/java/tag/owasp/RSPEC-2755 + * * @return XPathFactory * @throws XPathFactoryConfigurationException */ private static XPathFactory getSecureXPathFactory() throws XPathFactoryConfigurationException { - XPathFactory xPathFactory = XPathFactory.newInstance(); + final XPathFactory xPathFactory = XPathFactory.newInstance(); xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); return xPathFactory; } From b3bddc75e136fa28d1aaaaa8712c581ed2474a64 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 7 Aug 2021 19:56:04 -0600 Subject: [PATCH 063/713] [maven-release-plugin] prepare release classgraph-4.8.112 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 72b6c1f18..7352ad26e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.112-SNAPSHOT + 4.8.112 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.112 From c9532a3b76d32f5a0925f5f4e7689a1f4d1cf9cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 7 Aug 2021 19:56:07 -0600 Subject: [PATCH 064/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7352ad26e..bd284b60a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.112 + 4.8.113-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.112 + classgraph-4.8.105 From 8fbfa7a5ee1c6ee34c137829f126a833310fe805 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Aug 2021 02:01:01 +0000 Subject: [PATCH 065/713] Bump jmh-core from 1.32 to 1.33 Bumps jmh-core from 1.32 to 1.33. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd284b60a..61c72000b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.32 + 1.33 test From 8d8dd4bbce9d2615f7535da211fe55f3a239fc8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Aug 2021 02:01:04 +0000 Subject: [PATCH 066/713] Bump jmh-generator-annprocess from 1.32 to 1.33 Bumps jmh-generator-annprocess from 1.32 to 1.33. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd284b60a..f1a8f3899 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.32 + 1.33 test From e414e16451be17523d0214b7d6f394e4198a4938 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 10 Aug 2021 11:23:41 -0600 Subject: [PATCH 067/713] Remove obsolete exception --- src/main/java/io/github/classgraph/ClassGraph.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 83d00c885..04fc3a0fc 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -640,10 +640,6 @@ public ClassGraph acceptPackages(final String... packageNames) { enableClassInfo(); for (final String packageName : packageNames) { final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName); - if (packageNameNormalized.startsWith("!") || packageNameNormalized.startsWith("-")) { - throw new IllegalArgumentException( - "This style of accepting/rejecting is no longer supported: " + packageNameNormalized); - } // Accept package scanSpec.packageAcceptReject.addToAccept(packageNameNormalized); final String path = AcceptReject.packageNameToPath(packageNameNormalized); From 07bbc1008546a62c12829837fa00b2fe7cd89c72 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 10 Aug 2021 11:37:28 -0600 Subject: [PATCH 068/713] Remove `IllegalArgumentException` for wrong class type (#543) --- src/main/java/io/github/classgraph/ClassInfo.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 271c5aa7c..bd71f11bc 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1685,9 +1685,6 @@ public ClassInfoList getInterfaces() { * interface, otherwise returns the empty list. */ public ClassInfoList getClassesImplementing() { - if (!isInterface()) { - throw new IllegalArgumentException("Class is not an interface: " + getName()); - } // Subclasses of implementing classes also implement the interface final ReachableAndDirectlyRelatedClasses implementingClasses = this .filterClassInfo(RelType.CLASSES_IMPLEMENTING, /* strictAccept = */ !isExternalClass); @@ -1917,9 +1914,6 @@ public ClassInfoList getClassesWithAnnotation() { if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - if (!isAnnotation()) { - throw new IllegalArgumentException("Class is not an annotation: " + getName()); - } // Get classes that have this annotation final ReachableAndDirectlyRelatedClasses classesWithAnnotation = this From 914251488c1b5aa2da941afc06ca10fc3b84f699 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 10 Aug 2021 11:38:33 -0600 Subject: [PATCH 069/713] [maven-release-plugin] prepare release classgraph-4.8.113 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e45630204..9e126b5e1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.113-SNAPSHOT + 4.8.113 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.113 From 168d168828a99b5fc482121ad8f40b39054862bb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 10 Aug 2021 11:38:35 -0600 Subject: [PATCH 070/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9e126b5e1..df67d5086 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.113 + 4.8.114-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.113 + classgraph-4.8.105 From 0dff2ef90188686d72c9262dd2e20fc5ab20831c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Aug 2021 02:01:44 +0000 Subject: [PATCH 071/713] Bump slf4j-api from 2.0.0-alpha2 to 2.0.0-alpha3 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha2 to 2.0.0-alpha3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits/v_2.0.0-alpha3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df67d5086..cc3367ccf 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.slf4j slf4j-api - 2.0.0-alpha2 + 2.0.0-alpha3 test From cb4fa77831804a6c80a247a3346827fad514b1e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Aug 2021 02:01:46 +0000 Subject: [PATCH 072/713] Bump slf4j-jdk14 from 2.0.0-alpha2 to 2.0.0-alpha3 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha2 to 2.0.0-alpha3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits/v_2.0.0-alpha3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df67d5086..c9b00de37 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha2 + 2.0.0-alpha3 test From b9b93e555bc45a889f29df1fbf1089dd910fbe5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Aug 2021 02:01:16 +0000 Subject: [PATCH 073/713] Bump slf4j-jdk14 from 2.0.0-alpha3 to 2.0.0-alpha4 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha3 to 2.0.0-alpha4. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha3...v_2.0.0-alpha4) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e587910b5..c0ebfc04e 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha3 + 2.0.0-alpha4 test From a9781873c6fd4f983669fc757f7994bb976e3a95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Aug 2021 02:01:19 +0000 Subject: [PATCH 074/713] Bump slf4j-api from 2.0.0-alpha3 to 2.0.0-alpha4 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha3 to 2.0.0-alpha4. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha3...v_2.0.0-alpha4) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e587910b5..8ed6ff24e 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.slf4j slf4j-api - 2.0.0-alpha3 + 2.0.0-alpha4 test From e0557fe03950eca0fa1f4183fab9bf028df28bf6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 14 Aug 2021 00:08:51 -0600 Subject: [PATCH 075/713] Fix reading of short values from constant pool table (#548) --- .../java/io/github/classgraph/Classfile.java | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 9efc0dac0..736a3b868 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -824,24 +824,6 @@ private boolean constantPoolStringEquals(final int cpIdx, final String asciiStr) // ------------------------------------------------------------------------------------------------------------- - /** - * Read an unsigned short from the constant pool. - * - * @param cpIdx - * the constant pool index. - * @return the unsigned short - * @throws IOException - * If an I/O exception occurred. - */ - private int cpReadUnsignedShort(final int cpIdx) throws IOException { - if (cpIdx < 1 || cpIdx >= cpCount) { - throw new ClassfileFormatException("Constant pool index " + cpIdx + ", should be in range [1, " - + (cpCount - 1) + "] -- cannot continue reading class. " - + "Please report this at https://github.com/classgraph/classgraph/issues"); - } - return reader.readUnsignedShort(entryOffset[cpIdx]); - } - /** * Read an int from the constant pool. * @@ -985,7 +967,7 @@ private Object readAnnotationElementValue() throws IOException { case 'J': return cpReadLong(reader.readUnsignedShort()); case 'S': - return (short) cpReadUnsignedShort(reader.readUnsignedShort()); + return (short) cpReadInt(reader.readUnsignedShort()); case 'Z': return cpReadInt(reader.readUnsignedShort()) != 0; case 's': From 68dcf109a597ab7d14e97b756e7a02878ffc35b8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 14 Aug 2021 00:09:34 -0600 Subject: [PATCH 076/713] [maven-release-plugin] prepare release classgraph-4.8.114 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fccb8dac3..9583e842b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.114-SNAPSHOT + 4.8.114 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.114 From 4217d6d0812e90f0dfe1b52d5a87b19cce4a30b2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 14 Aug 2021 00:09:37 -0600 Subject: [PATCH 077/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9583e842b..4367b14fa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.114 + 4.8.115-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.114 + classgraph-4.8.105 From 82aa678f1d2105e5e183625a23b5f6cf9596d32b Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Mon, 16 Aug 2021 01:08:10 +0200 Subject: [PATCH 078/713] Add typed overloads for methods taking class names --- .../github/classgraph/AnnotationInfoList.java | 13 ++ .../java/io/github/classgraph/ClassInfo.java | 158 +++++++++++++++++- .../java/io/github/classgraph/FieldInfo.java | 45 ++++- .../java/io/github/classgraph/MethodInfo.java | 54 +++++- .../classgraph/MethodParameterInfo.java | 47 +++++- .../java/io/github/classgraph/ModuleInfo.java | 29 +++- .../io/github/classgraph/PackageInfo.java | 28 +++- .../java/io/github/classgraph/ScanResult.java | 74 +++++++- .../io/github/classgraph/utils/Assert.java | 16 ++ .../MethodParameterAnnotationsTest.java | 4 +- .../github/classgraph/issues/IssuesTest.java | 8 +- .../issues/issue101/Issue101Test.java | 6 +- .../issues/issue107/Issue107Test.java | 2 +- .../issues/issue216/Issue216Test.java | 2 +- .../classgraph/issues/issue318/Issue318.java | 2 +- .../classgraph/issues/issue350/Issue350.java | 8 +- .../classgraph/issues/issue93/Issue93.java | 8 +- .../classgraph/test/ClassGraphTest.java | 6 +- .../AnnotationClassRefTest.java | 2 +- .../FieldAndMethodAnnotationTest.java | 6 +- .../test/internal/InternalExternalTest.java | 4 +- .../MethodAnnotationTest.java | 4 +- .../TestMethodMetaAnnotation.java | 6 +- ...cyForFunctionParameterAnnotationsTest.java | 12 +- 24 files changed, 486 insertions(+), 58 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/utils/Assert.java diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index 03b60fb5b..0bc31c421 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.util.ArrayList; import java.util.HashSet; @@ -36,6 +37,7 @@ import java.util.Set; import io.github.classgraph.ClassInfo.RelType; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -344,6 +346,17 @@ public AnnotationInfoList directOnly() { // ------------------------------------------------------------------------------------------------------------- + /** + * Get the {@link Repeatable} annotation with the given class, or the empty list if none found. + * + * @param annotationClass The class to search for. + * @return The list of annotations with the given class, or the empty list if none found. + */ + public AnnotationInfoList getRepeatable(final Class annotationClass) { + Assert.isAnnotation(annotationClass); + return getRepeatable(annotationClass.getName()); + } + /** * Get the {@link Repeatable} annotation with the given name, or the empty list if none found. * diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index bd71f11bc..fc6c7ae53 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.io.File; +import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.lang.annotation.Repeatable; import java.lang.reflect.Modifier; @@ -56,6 +57,7 @@ import nonapi.io.github.classgraph.types.Parser; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.LogNode; /** Holds metadata about a class encountered during a scan. */ @@ -1291,6 +1293,16 @@ public boolean isArrayClass() { return this instanceof ArrayClassInfo; } + /** + * Checks if this class extends the superclass. + * + * @param superclass A superclass. + * @return true if this class extends the superclass. + */ + public boolean extendsSuperclass(final Class superclass) { + return extendsSuperclass(superclass.getName()); + } + /** * Checks if this class extends the named superclass. * @@ -1348,6 +1360,17 @@ public boolean isImplementedInterface() { return relatedClasses.get(RelType.CLASSES_IMPLEMENTING) != null || isInterface(); } + /** + * Checks whether this class implements the interface. + * + * @param interfaceClazz An interface. + * @return true if this class implements the interface. + */ + public boolean implementsInterface(final Class interfaceClazz) { + Assert.isInterface(interfaceClazz); + return implementsInterface(interfaceClazz.getName()); + } + /** * Checks whether this class implements the named interface. * @@ -1359,6 +1382,17 @@ public boolean implementsInterface(final String interfaceName) { return getInterfaces().containsName(interfaceName); } + /** + * Checks whether this class has the annotation. + * + * @param annotation An annotation. + * @return true if this class has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Checks whether this class has the named annotation. * @@ -1397,6 +1431,17 @@ public boolean hasField(final String fieldName) { return false; } + /** + * Checks whether this class declares a field with the annotation. + * + * @param annotation A field annotation. + * @return true if this class declares a field with the annotation. + */ + public boolean hasDeclaredFieldAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasDeclaredFieldAnnotation(annotation.getName()); + } + /** * Checks whether this class declares a field with the named annotation. * @@ -1413,6 +1458,17 @@ public boolean hasDeclaredFieldAnnotation(final String fieldAnnotationName) { return false; } + /** + * Checks whether this class or one of its superclasses declares a field with the annotation. + * + * @param fieldAnnotation A field annotation. + * @return true if this class or one of its superclasses declares a field with the annotation. + */ + public boolean hasFieldAnnotation(final Class fieldAnnotation) { + Assert.isAnnotation(fieldAnnotation); + return hasFieldAnnotation(fieldAnnotation.getName()); + } + /** * Checks whether this class or one of its superclasses declares a field with the named annotation. * @@ -1456,6 +1512,17 @@ public boolean hasMethod(final String methodName) { return false; } + /** + * Checks whether this class declares a method with the annotation. + * + * @param methodAnnotation A method annotation. + * @return true if this class declares a method with the annotation. + */ + public boolean hasDeclaredMethodAnnotation(final Class methodAnnotation) { + Assert.isAnnotation(methodAnnotation); + return hasDeclaredMethodAnnotation(methodAnnotation.getName()); + } + /** * Checks whether this class declares a method with the named annotation. * @@ -1472,6 +1539,19 @@ public boolean hasDeclaredMethodAnnotation(final String methodAnnotationName) { return false; } + /** + * Checks whether this class or one of its superclasses or interfaces declares a method with the + * annotation. + * + * @param methodAnnotation A method annotation. + * @return true if this class or one of its superclasses or interfaces declares a method with the + * annotation. + */ + public boolean hasMethodAnnotation(final Class methodAnnotation) { + Assert.isAnnotation(methodAnnotation); + return hasMethodAnnotation(methodAnnotation.getName()); + } + /** * Checks whether this class or one of its superclasses or interfaces declares a method with the named * annotation. @@ -1490,6 +1570,17 @@ public boolean hasMethodAnnotation(final String methodAnnotationName) { return false; } + /** + * Checks whether this class declares a method with the annotation. + * + * @param methodParameterAnnotation A method annotation. + * @return true if this class declares a method with the annotation. + */ + public boolean hasDeclaredMethodParameterAnnotation(final Class methodParameterAnnotation) { + Assert.isAnnotation(methodParameterAnnotation); + return hasDeclaredMethodParameterAnnotation(methodParameterAnnotation.getName()); + } + /** * Checks whether this class declares a method with the named annotation. * @@ -1506,6 +1597,17 @@ public boolean hasDeclaredMethodParameterAnnotation(final String methodParameter return false; } + /** + * Checks whether this class or one of its superclasses or interfaces has a method with the annotation. + * + * @param methodParameterAnnotation A method annotation. + * @return true if this class or one of its superclasses or interfaces has a method with the annotation. + */ + public boolean hasMethodParameterAnnotation(final Class methodParameterAnnotation) { + Assert.isAnnotation(methodParameterAnnotation); + return hasMethodParameterAnnotation(methodParameterAnnotation.getName()); + } + /** * Checks whether this class or one of its superclasses or interfaces has a method with the named annotation. * @@ -1837,7 +1939,7 @@ public AnnotationInfoList getAnnotationInfo() { } /** - * Get a the named non-{@link Repeatable} annotation on this class, or null if the class does not have the named + * Get a the non-{@link Repeatable} annotation on this class, or null if the class does not have the * annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) * *

@@ -1845,10 +1947,33 @@ public AnnotationInfoList getAnnotationInfo() { * its subclasses. * *

+ * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, + * and then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list + * doesn't have to be built multiple times. + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this class, or null if the + * class does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + + /** + * Get a the named non-{@link Repeatable} annotation on this class, or null if the class does not have the named + * annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) + * + *

+ * Also handles the {@link Inherited} meta-annotation, which causes an annotation to annotate a class and all of + * its subclasses. + * + *

* Note that if you need to get multiple named annotations, it is faster to call {@link #getAnnotationInfo()}, * and then get the named annotations from the returned {@link AnnotationInfoList}, so that the returned list * doesn't have to be built multiple times. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this class, or null if the @@ -1859,18 +1984,41 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { } /** - * Get a the named {@link Repeatable} annotation on this class, or the empty list if the class does not have the - * named annotation. + * Get a the {@link Repeatable} annotation on this class, or the empty list if the class does not have the + * annotation. * *

* Also handles the {@link Inherited} meta-annotation, which causes an annotation to annotate a class and all of * its subclasses. * *

+ * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, + * and then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list + * doesn't have to be built multiple times. + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this class, or the empty + * list if the class does not have the annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfoRepeatable(annotation.getName()); + } + + /** + * Get a the named {@link Repeatable} annotation on this class, or the empty list if the class does not have the + * named annotation. + * + *

+ * Also handles the {@link Inherited} meta-annotation, which causes an annotation to annotate a class and all of + * its subclasses. + * + *

* Note that if you need to get multiple named annotations, it is faster to call {@link #getAnnotationInfo()}, * and then get the named annotations from the returned {@link AnnotationInfoList}, so that the returned list * doesn't have to be built multiple times. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfoList} of all instances of the named annotation on this class, or the empty diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 519100b00..a6f960181 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -40,6 +41,7 @@ import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.LogNode; /** @@ -370,10 +372,24 @@ public AnnotationInfoList getAnnotationInfo() { : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); } + /** + * Get a the non-{@link Repeatable} annotation on this field, or null if the field does not have the + * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this field, or null if the + * field does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + /** * Get a the named non-{@link Repeatable} annotation on this field, or null if the field does not have the named * annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this field, or null if the @@ -383,10 +399,24 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { return getAnnotationInfo().get(annotationName); } + /** + * Get a the {@link Repeatable} annotation on this field, or the empty list if the field does not have the + * annotation. + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this field, or the empty + * list if the field does not have the annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfoRepeatable(annotation.getName()); + } + /** * Get a the named {@link Repeatable} annotation on this field, or the empty list if the field does not have the * named annotation. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfoList} of all instances of the named annotation on this field, or the empty @@ -396,6 +426,17 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam return getAnnotationInfo().getRepeatable(annotationName); } + /** + * Check if the field has a given annotation. + * + * @param annotation The annotation. + * @return true if this field has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Check if the field has a given named annotation. * diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 577360ebb..754d3d3f6 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -42,6 +43,7 @@ import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.LogNode; /** @@ -572,10 +574,23 @@ public AnnotationInfoList getAnnotationInfo() { : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); } + /** + * Get a the non-{@link Repeatable} annotation on this method, or null if the method does not have the + * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) + * + * @param annotation The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this method, or null if the + * method does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + /** * Get a the named non-{@link Repeatable} annotation on this method, or null if the method does not have the * named annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this method, or null if the @@ -585,10 +600,23 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { return getAnnotationInfo().get(annotationName); } + /** + * Get a the {@link Repeatable} annotation on this method, or the empty list if the method does not have + * the annotation. + * + * @param annotation The annotation. + * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method, or the + * empty list if the method does not have the annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfoRepeatable(annotation.getName()); + } + /** * Get a the named {@link Repeatable} annotation on this method, or the empty list if the method does not have * the named annotation. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfoList} containing all instances of the named annotation on this method, or the @@ -598,6 +626,17 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam return getAnnotationInfo().getRepeatable(annotationName); } + /** + * Check if this method has the annotation. + * + * @param annotation The annotation. + * @return true if this method has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Check if this method has the named annotation. * @@ -609,6 +648,17 @@ public boolean hasAnnotation(final String annotationName) { return getAnnotationInfo().containsName(annotationName); } + /** + * Check if this method has a parameter with the annotation. + * + * @param annotation The method parameter annotation. + * @return true if this method has a parameter with the annotation. + */ + public boolean hasParameterAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasParameterAnnotation(annotation.getName()); + } + /** * Check if this method has a parameter with the named annotation. * diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index abb48f037..652397f77 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -28,6 +28,9 @@ */ package io.github.classgraph; +import nonapi.io.github.classgraph.utils.Assert; + +import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.Modifier; import java.util.Arrays; @@ -178,11 +181,26 @@ public AnnotationInfoList getAnnotationInfo() { } } + /** + * Get a the non-{@link Repeatable} annotation on this method, or null if the method parameter does not + * have the annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} + * annotations.) + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this method parameter, or null + * if the method parameter does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + /** * Get a the named non-{@link Repeatable} annotation on this method, or null if the method parameter does not * have the named annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} * annotations.) - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this method parameter, or null @@ -192,10 +210,24 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { return getAnnotationInfo().get(annotationName); } + /** + * Get a the {@link Repeatable} annotation on this method, or the empty list if the method parameter does + * not have the annotation. + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method + * parameter, or the empty list if the method parameter does not have the annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfoRepeatable(annotation.getName()); + } + /** * Get a the named {@link Repeatable} annotation on this method, or the empty list if the method parameter does * not have the named annotation. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfoList} containing all instances of the named annotation on this method @@ -205,6 +237,17 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam return getAnnotationInfo().getRepeatable(annotationName); } + /** + * Check whether this method parameter has the annotation. + * + * @param annotation The annotation. + * @return true if this method parameter has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Check whether this method parameter has the named annotation. * diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 272e7c65b..8b94c725b 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -28,11 +28,13 @@ */ package io.github.classgraph; +import java.lang.annotation.Annotation; import java.net.URI; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.CollectionUtils; /** Holds metadata about a package encountered during a scan. */ @@ -231,9 +233,21 @@ void addAnnotations(final AnnotationInfoList moduleAnnotations) { } } + /** + * Get a the annotation on this module, or null if the module does not have the annotation. + * + * @param annotation The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this module, or null if the + * module does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + /** * Get a the named annotation on this module, or null if the module does not have the named annotation. - * + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this module, or null if the @@ -260,9 +274,20 @@ public AnnotationInfoList getAnnotationInfo() { return annotationInfo; } + /** + * Check if this module has the annotation. + * + * @param annotation The annotation. + * @return true if this module has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Check if this module has the named annotation. - * + * * @param annotationName * The name of an annotation. * @return true if this module has the named annotation. diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index ed7f0b604..7d180720c 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.lang.annotation.Annotation; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -36,6 +37,7 @@ import java.util.Set; import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.CollectionUtils; /** Holds metadata about a package encountered during a scan. */ @@ -123,8 +125,21 @@ void addClassInfo(final ClassInfo classInfo) { // ------------------------------------------------------------------------------------------------------------- /** - * Get a the named annotation on this package, or null if the package does not have the named annotation. + * Get a the annotation on this package, or null if the package does not have the annotation. * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this package, or null if the + * package does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + + /** + * Get a the named annotation on this package, or null if the package does not have the named annotation. + * * @param annotationName * The annotation name. * @return An {@link AnnotationInfo} object representing the named annotation on this package, or null if the @@ -151,6 +166,17 @@ public AnnotationInfoList getAnnotationInfo() { return annotationInfo; } + /** + * Check if the package has the annotation. + * + * @param annotation The annotation. + * @return true if this package has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + /** * Check if the package has the named annotation. * diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index f54cdfd26..1a34e24cb 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -30,6 +30,7 @@ import java.io.Closeable; import java.io.File; +import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URI; @@ -56,10 +57,7 @@ import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.utils.CollectionUtils; -import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.JarUtils; -import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.*; /** * The result of a scan. You should assign a ScanResult in a try-with-resources block, or manually close it when you @@ -882,6 +880,16 @@ public ClassInfoList getAllStandardClasses() { return ClassInfo.getAllStandardClasses(classNameToClassInfo.values(), scanSpec); } + /** + * Get all subclasses of the superclass. + * + * @param superclass The superclass. + * @return A list of subclasses of the superclass, or the empty list if none. + */ + public ClassInfoList getSubclasses(final Class superclass) { + return getSubclasses(superclass.getName()); + } + /** * Get all subclasses of the named superclass. * @@ -923,6 +931,17 @@ public ClassInfoList getSuperclasses(final String subclassName) { return subclass == null ? ClassInfoList.EMPTY_LIST : subclass.getSuperclasses(); } + /** + * Get classes that have a method with an annotation of the named type. + * + * @param methodAnnotation the method annotation. + * @return A list of classes with a method that has an annotation of the named type, or the empty list if none. + */ + public ClassInfoList getClassesWithMethodAnnotation(final Class methodAnnotation) { + Assert.isAnnotation(methodAnnotation); + return getClassesWithMethodAnnotation(methodAnnotation.getName()); + } + /** * Get classes that have a method with an annotation of the named type. * @@ -942,6 +961,18 @@ public ClassInfoList getClassesWithMethodAnnotation(final String methodAnnotatio return classInfo == null ? ClassInfoList.EMPTY_LIST : classInfo.getClassesWithMethodAnnotation(); } + /** + * Get classes that have a method with a parameter that is annotated with an annotation of the named type. + * + * @param methodParameterAnnotation the method parameter annotation. + * @return A list of classes that have a method with a parameter annotated with the named annotation type, or + * the empty list if none. + */ + public ClassInfoList getClassesWithMethodParameterAnnotation(final Class methodParameterAnnotation) { + Assert.isAnnotation(methodParameterAnnotation); + return getClassesWithMethodParameterAnnotation(methodParameterAnnotation.getName()); + } + /** * Get classes that have a method with a parameter that is annotated with an annotation of the named type. * @@ -962,6 +993,17 @@ public ClassInfoList getClassesWithMethodParameterAnnotation(final String method return classInfo == null ? ClassInfoList.EMPTY_LIST : classInfo.getClassesWithMethodParameterAnnotation(); } + /** + * Get classes that have a field with an annotation of the named type. + * + * @param fieldAnnotation the field annotation. + * @return A list of classes that have a field with an annotation of the named type, or the empty list if none. + */ + public ClassInfoList getClassesWithFieldAnnotation(final Class fieldAnnotation) { + Assert.isAnnotation(fieldAnnotation); + return getClassesWithFieldAnnotation(fieldAnnotation.getName()); + } + /** * Get classes that have a field with an annotation of the named type. * @@ -1020,6 +1062,18 @@ public ClassInfoList getInterfaces(final String className) { return classInfo == null ? ClassInfoList.EMPTY_LIST : classInfo.getInterfaces(); } + /** + * Get all classes that implement (or have superclasses that implement) the interface (or one of its + * subinterfaces). + * + * @param interfaceClass The interface class. + * @return A list of all classes that implement the interface, or the empty list if none. + */ + public ClassInfoList getClassesImplementing(final Class interfaceClass) { + Assert.isInterface(interfaceClass); + return getClassesImplementing(interfaceClass.getName()); + } + /** * Get all classes that implement (or have superclasses that implement) the named interface (or one of its * subinterfaces). @@ -1075,6 +1129,18 @@ public ClassInfoList getAllInterfacesAndAnnotations() { return ClassInfo.getAllInterfacesOrAnnotationClasses(classNameToClassInfo.values(), scanSpec); } + /** + * Get classes with the class annotation or meta-annotation. + * + * @param annotation The class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the class annotation during the scan, + * or the empty list if none. + */ + public ClassInfoList getClassesWithAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return getClassesWithAnnotation(annotation.getName()); + } + /** * Get classes with the named class annotation or meta-annotation. * diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java new file mode 100644 index 000000000..204eaa38f --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java @@ -0,0 +1,16 @@ +package nonapi.io.github.classgraph.utils; + +public final class Assert { + + public static void isAnnotation(Class clazz) { + if (!clazz.isAnnotation()) { + throw new IllegalArgumentException(clazz + " is not an annotation"); + } + } + + public static void isInterface(Class clazz) { + if (!clazz.isInterface()) { + throw new IllegalArgumentException(clazz + " is not an interface"); + } + } +} diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java index f4d0d3cb4..9373a1d7c 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java @@ -68,9 +68,9 @@ public void annotationEquality() { .containsOnly(W.class.getName()); assertThat(scanResult.getClassInfo(Z.class.getName()).getMethodParameterAnnotations().getNames()) .containsOnly(X.class.getName()); - assertThat(scanResult.getClassesWithMethodParameterAnnotation(W.class.getName()).getNames()) + assertThat(scanResult.getClassesWithMethodParameterAnnotation(W.class).getNames()) .containsOnly(Y.class.getName()); - assertThat(scanResult.getClassesWithMethodParameterAnnotation(X.class.getName()).getNames()) + assertThat(scanResult.getClassesWithMethodParameterAnnotation(X.class).getNames()) .containsOnly(Z.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/IssuesTest.java b/src/test/java/io/github/classgraph/issues/IssuesTest.java index 1ca9adecc..6a8291f5a 100644 --- a/src/test/java/io/github/classgraph/issues/IssuesTest.java +++ b/src/test/java/io/github/classgraph/issues/IssuesTest.java @@ -21,7 +21,7 @@ public class IssuesTest { @Test public void issue70() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Impl1.class.getPackage().getName()).scan()) { - assertThat(scanResult.getSubclasses(Object.class.getName()).getNames()).contains(Impl1.class.getName()); + assertThat(scanResult.getSubclasses(Object.class).getNames()).contains(Impl1.class.getName()); } } @@ -32,7 +32,7 @@ public void issue70() { public void issue70EnableExternalClasses() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Impl1.class.getPackage().getName()) .enableExternalClasses().scan()) { - assertThat(scanResult.getSubclasses(Object.class.getName()).getNames()).contains(Impl1.class.getName()); + assertThat(scanResult.getSubclasses(Object.class).getNames()).contains(Impl1.class.getName()); assertThat(scanResult.getSuperclasses(Impl1Sub.class.getName()).getNames()) .containsOnly(Impl1.class.getName()); } @@ -70,7 +70,7 @@ public void extendsExternalWithEnableExternal() { public void extendsExternalSubclass() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { - assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(ExternalSuperclass.class).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); } } @@ -83,7 +83,7 @@ public void nonStrictExtendsExternalSubclass() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() .scan()) { - assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(ExternalSuperclass.class).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java index 421326983..645406af9 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -46,7 +46,7 @@ public class Issue101Test { public void nonInheritedAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getClassesWithAnnotation(NonInheritedAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(NonInheritedAnnotation.class).getNames()) .containsOnly(AnnotatedClass.class.getName()); } } @@ -58,7 +58,7 @@ public void nonInheritedAnnotation() { public void inheritedMetaAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class.getName()) + assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class) .getStandardClasses().getNames()).containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName()); } @@ -71,7 +71,7 @@ public void inheritedMetaAnnotation() { public void inheritedAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class).getNames()) .containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName(), AnnotatedInterface.class.getName()); } diff --git a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java index a53864c14..729c8d85f 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -51,7 +51,7 @@ public void issue107Test() { // package-info is a non-public class .ignoreClassVisibility() // .scan()) { - assertThat(scanResult.getClassesWithAnnotation(PackageAnnotation.class.getName()).getNames()).isEmpty(); + assertThat(scanResult.getClassesWithAnnotation(PackageAnnotation.class).getNames()).isEmpty(); assertThat(scanResult.getPackageInfo().getNames()) .containsAll(Arrays.asList("io.github.classgraph", Issue107Test.class.getPackage().getName())); assertThat(scanResult.getPackageInfo(Issue107Test.class.getPackage().getName()).getAnnotationInfo() diff --git a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java index 0ead78230..f8818b24a 100644 --- a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java +++ b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java @@ -54,7 +54,7 @@ public void testSpringBootJarWithLibJars() { assertThat(result.getAllClasses().filter(new ClassInfoFilter() { @Override public boolean accept(final ClassInfo ci) { - return ci.hasAnnotation(Entity.class.getName()); + return ci.hasAnnotation(Entity.class); } }).getNames()).containsOnly(Issue216Test.class.getName()); } diff --git a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java index b207e598d..b0553b7a5 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -79,7 +79,7 @@ public void issue318() { .enableAnnotationInfo().enableClassInfo().ignoreClassVisibility() // //.verbose() // .scan()) { - assertThat(scanResult.getClassesWithAnnotation(MyAnn.class.getName()).getNames()).containsOnly( + assertThat(scanResult.getClassesWithAnnotation(MyAnn.class).getNames()).containsOnly( With1MyAnn.class.getName(), With2MyAnn.class.getName(), With3MyAnn.class.getName()); assertThat(scanResult.getClassInfo(With3MyAnn.class.getName()) .getAnnotationInfoRepeatable(MyAnn.class.getName()).size()).isEqualTo(3); diff --git a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java index 5cbb2729e..73fef1549 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -70,17 +70,17 @@ public static class PrivSub extends Priv { public void test() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo().scan()) { - assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); - assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); } try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo() .ignoreFieldVisibility().ignoreMethodVisibility().scan()) { - assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); - assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java index 54ae8b607..3d1f40151 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java @@ -50,9 +50,9 @@ static class RetentionRuntimeAnnotated { public void classRetentionIsDefault() { try (ScanResult scanResult = new ClassGraph().acceptPackages(PKG).enableAnnotationInfo() .ignoreClassVisibility().scan()) { - assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class).getNames()) .containsOnly(RetentionClassAnnotated.class.getName()); - assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class).getNames()) .containsOnly(RetentionRuntimeAnnotated.class.getName()); } } @@ -65,8 +65,8 @@ public void classRetentionIsDefault() { public void classRetentionIsNotVisibleWithRetentionPolicyRUNTIME() { try (ScanResult scanResult = new ClassGraph().acceptPackages(PKG).enableAnnotationInfo() .ignoreClassVisibility().disableRuntimeInvisibleAnnotations().scan()) { - assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class).getNames()) .containsOnly(RetentionRuntimeAnnotated.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 1a4ab2170..8ce8f72e7 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -236,7 +236,7 @@ public void testAcceptedWithoutExceptionWithoutStrictAccept() { public void testCanQueryWithRejectedAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) .containsExactly(Accepted.class.getName()); } } @@ -302,7 +302,7 @@ public void testRejectedPackage() { assertThat(scanResult.getSubclasses(Accepted.class.getName()).getNames()).isEmpty(); assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) .isEmpty(); } } @@ -343,7 +343,7 @@ public void testVisibleIfNotRejected() { .containsExactly(RejectedSubinterface.class.getName()); assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()) .containsExactly(RejectedSubinterface.class.getName()); - assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) .containsExactly(Accepted.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java index 133cfea2c..1153a5154 100644 --- a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java +++ b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java @@ -86,7 +86,7 @@ public void testClassRefAnnotation() { .acceptPackages(AnnotationClassRefTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final ClassInfoList testClasses = scanResult - .getClassesWithMethodAnnotation(ClassRefAnnotation.class.getName()); + .getClassesWithMethodAnnotation(ClassRefAnnotation.class); assertThat(testClasses.size()).isEqualTo(1); final ClassInfo testClass = testClasses.get(0); final MethodInfo method = testClass.getMethodInfo().getSingleMethod("methodWithAnnotation"); diff --git a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java index 360ee5e01..6b7550b14 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -61,7 +61,7 @@ public void testGetNamesOfClassesWithFieldAnnotation() { .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult - .getClassesWithFieldAnnotation(ExternalAnnotation.class.getName()).getNames(); + .getClassesWithFieldAnnotation(ExternalAnnotation.class).getNames(); assertThat(testClasses).isEmpty(); } } @@ -75,7 +75,7 @@ public void testGetNamesOfClassesWithFieldAnnotationIgnoringVisibility() { .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .ignoreFieldVisibility().enableAnnotationInfo().scan()) { final List testClasses = scanResult - .getClassesWithFieldAnnotation(ExternalAnnotation.class.getName()).getNames(); + .getClassesWithFieldAnnotation(ExternalAnnotation.class).getNames(); assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } @@ -90,7 +90,7 @@ public void testGetNamesOfClassesWithMethodAnnotation() { .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult - .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()).getNames(); + .getClassesWithMethodAnnotation(ExternalAnnotation.class).getNames(); assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java index 9ce34401b..d5dacdf7a 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -61,7 +61,7 @@ public void testAcceptingExternalClassesWithoutEnablingExternalClasses() { assertThat(scanResult.getClassesImplementing(ExternalInterface.class.getName()).getNames()) .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class).getNames()) .containsOnly(InternalAnnotatedByExternal.class.getName()); } } @@ -82,7 +82,7 @@ public void testIncludeReferencedClasses() { .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()) .doesNotContain(ExternalAnnotation.class.getName()); - assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class.getName()).getNames()) + assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class).getNames()) .containsOnly(InternalAnnotatedByExternal.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java index c7f5bde00..0148dbaf6 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -54,7 +54,7 @@ public void testGetNamesOfClassesWithMethodAnnotation() { .acceptPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() .enableMethodInfo().enableAnnotationInfo().scan()) { final List testClasses = scanResult - .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()).getNames(); + .getClassesWithMethodAnnotation(ExternalAnnotation.class).getNames(); assertThat(testClasses).isEmpty(); } } @@ -68,7 +68,7 @@ public void testGetNamesOfClassesWithMethodAnnotationIgnoringVisibility() { .acceptPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() .enableMethodInfo().enableAnnotationInfo().ignoreMethodVisibility().scan()) { final ClassInfoList classesWithMethodAnnotation = scanResult - .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()); + .getClassesWithMethodAnnotation(ExternalAnnotation.class); final List testClasses = classesWithMethodAnnotation.getNames(); assertThat(testClasses).containsOnly(MethodAnnotationTest.class.getName()); boolean found = false; diff --git a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java index 2dae751f4..03f839691 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -107,7 +107,7 @@ public void testMetaAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() .scan()) { - final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) + final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class) .getNames(); assertThat(testClasses).containsOnly(MethodAnnotation.class.getName(), ClassAnnotation.class.getName(), MetaAnnotatedClass.class.getName()); @@ -123,7 +123,7 @@ public void testMetaAnnotationStandardClassesOnly() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() .scan()) { - final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) + final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class) .getStandardClasses().getNames(); assertThat(testClasses).containsOnly(MetaAnnotatedClass.class.getName()); } @@ -139,7 +139,7 @@ public void testMethodMetaAnnotation() { .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult - .getClassesWithMethodAnnotation(MetaAnnotation.class.getName()).getNames(); + .getClassesWithMethodAnnotation(MetaAnnotation.class).getNames(); assertThat(testClasses).containsOnly(ClassWithMetaAnnotatedMethod.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java index 53e3399c1..a2bb6aaa2 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -112,7 +112,7 @@ public void canDetect_ParameterAnnotation_WithRuntimeRetention() { final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("parameterAnnotation_WithRuntimeRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class)).isTrue(); } /** @@ -142,9 +142,9 @@ public void canDetect_TwoAnnotations_WithRuntimeRetention_ForSingleParam() { final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("twoAnnotations_WithRuntimeRetention_ForSingleParam"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class)).isTrue(); - assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class)).isTrue(); } /** @@ -168,7 +168,7 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention( final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneClassRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class)).isTrue(); } /** @@ -193,7 +193,7 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention_ final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class)).isTrue(); } /** @@ -217,7 +217,7 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneSourceRetention final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneSourceRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class)).isTrue(); } /** From c7e8c233092c05f1484dc4b4232c38200687076d Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Mon, 16 Aug 2021 01:25:55 +0200 Subject: [PATCH 079/713] use new overloads in tests --- .../issues/issue261/Issue261Test.java | 2 +- .../classgraph/issues/issue314/Issue314.java | 4 +- .../classgraph/issues/issue318/Issue318.java | 2 +- .../issues/issue370/Issue370Test.java | 2 +- .../issues/issue38/Issue38Test.java | 2 +- .../issues/issue74/Issue74Test.java | 2 +- .../classgraph/test/ClassGraphTest.java | 68 +++++++++---------- .../github/classgraph/test/ClassInfoTest.java | 4 +- .../test/internal/InternalExternalTest.java | 8 +-- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java index 6f52e4580..d2415fad0 100644 --- a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java +++ b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java @@ -64,7 +64,7 @@ private static class Cls extends SuperCls { public void issue261Test() { // Accept only the class Cls, so that SuperCls and SuperSuperCls are external classes try (ScanResult scanResult = new ClassGraph().acceptClasses(Cls.class.getName()).enableAllInfo().scan()) { - assertThat(scanResult.getSubclasses(SuperSuperCls.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(SuperSuperCls.class).getNames()) .containsOnly(SuperCls.class.getName(), Cls.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java b/src/test/java/io/github/classgraph/issues/issue314/Issue314.java index d0016228c..97727bc6a 100644 --- a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java +++ b/src/test/java/io/github/classgraph/issues/issue314/Issue314.java @@ -44,8 +44,8 @@ public void issue314() { try (final ScanResult scanResult2 = ScanResult.fromJSON(scanResult1.toJSON())) { final String json2 = scanResult2.toJSON(2); assertThat(json1).isEqualTo(json2); - assertThat(scanResult1.getSubclasses(A.class.getName()).getNames()).containsOnly(B.class.getName()); - assertThat(scanResult2.getSubclasses(A.class.getName()).getNames()).containsOnly(B.class.getName()); + assertThat(scanResult1.getSubclasses(A.class).getNames()).containsOnly(B.class.getName()); + assertThat(scanResult2.getSubclasses(A.class).getNames()).containsOnly(B.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java index b0553b7a5..b182f27cf 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -82,7 +82,7 @@ public void issue318() { assertThat(scanResult.getClassesWithAnnotation(MyAnn.class).getNames()).containsOnly( With1MyAnn.class.getName(), With2MyAnn.class.getName(), With3MyAnn.class.getName()); assertThat(scanResult.getClassInfo(With3MyAnn.class.getName()) - .getAnnotationInfoRepeatable(MyAnn.class.getName()).size()).isEqualTo(3); + .getAnnotationInfoRepeatable(MyAnn.class).size()).isEqualTo(3); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java index c099dc214..24d88eebb 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java +++ b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java @@ -54,7 +54,7 @@ public void issue370Test() { final ClassInfo clazzInfo = scanResult.getClassInfo(ClassWithAnnotation.class.getName()); assertThat(clazzInfo).isNotNull(); for (final MethodInfo methodInfo : clazzInfo.getMethodInfo().filter(MethodInfo::isPublic)) { - final AnnotationInfo annotationInfo = methodInfo.getAnnotationInfo(ApiOperation.class.getName()); + final AnnotationInfo annotationInfo = methodInfo.getAnnotationInfo(ApiOperation.class); final String value = annotationInfo.getParameterValues().get("notes").getValue().toString(); assertThat(value).isEqualTo("${snippetclassifications.findById}"); } diff --git a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java index 402e3bf22..f42ebf12d 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java +++ b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java @@ -29,7 +29,7 @@ public static abstract class AnnotationLiteral implements void testImplementsSuppressWarnings() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue38Test.class.getPackage().getName()) .scan()) { - assertThat(scanResult.getClassesImplementing(SuppressWarnings.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(SuppressWarnings.class).getNames()) .containsOnly(ImplementsSuppressWarnings.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java index 77be1fa95..99a8e6f6f 100644 --- a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java +++ b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java @@ -42,7 +42,7 @@ public class ImplementsFunction implements Function { public void issue74() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue74Test.class.getPackage().getName()) .scan()) { - assertThat(scanResult.getClassesImplementing(Function.class.getName()).getNames()).containsOnly( + assertThat(scanResult.getClassesImplementing(Function.class).getNames()).containsOnly( FunctionAdapter.class.getName(), ImplementsFunction.class.getName(), ExtendsFunctionAdapter.class.getName()); } diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 8ce8f72e7..94a0a412d 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -125,7 +125,7 @@ public void scanWithAcceptAndReject() { @Test public void scanSubAndSuperclasses() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { - final List subclasses = scanResult.getSubclasses(Cls.class.getName()).getNames(); + final List subclasses = scanResult.getSubclasses(Cls.class).getNames(); assertThat(subclasses).doesNotContain(Cls.class.getName()); assertThat(subclasses).contains(ClsSub.class.getName()); assertThat(subclasses).contains(ClsSubSub.class.getName()); @@ -142,7 +142,7 @@ public void scanSubAndSuperclasses() { @Test public void scanSubAndSuperinterface() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { - final List subinterfaces = scanResult.getClassesImplementing(Iface.class.getName()).getNames(); + final List subinterfaces = scanResult.getClassesImplementing(Iface.class).getNames(); assertThat(subinterfaces).doesNotContain(Iface.class.getName()); assertThat(subinterfaces).contains(IfaceSub.class.getName()); assertThat(subinterfaces).contains(IfaceSubSub.class.getName()); @@ -159,47 +159,47 @@ public void scanSubAndSuperinterface() { @Test public void scanTransitiveImplements() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .doesNotContain(Iface.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .doesNotContain(Cls.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl1.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .contains(Impl1.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .contains(Impl1.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl1Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .contains(Impl1Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .contains(Impl1Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl1SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .contains(Impl1SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .contains(Impl1SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl2.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .doesNotContain(Impl2.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .doesNotContain(Impl2.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl2Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .doesNotContain(Impl2Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .doesNotContain(Impl2Sub.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) .contains(Impl2SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .contains(Impl2SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .contains(Impl2SubSub.class.getName()); } } @@ -212,9 +212,9 @@ public void testExternalSuperclassReturned() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()) .containsExactly(RejectedSuperclass.class.getName()); - assertThat(scanResult.getSubclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); + assertThat(scanResult.getSubclasses(Accepted.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); } } @@ -249,9 +249,9 @@ public void testRejectedPlaceholderNotReturned() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ROOT_PACKAGE) .rejectPackages(RejectedAnnotation.class.getPackage().getName()).enableAnnotationInfo().scan()) { assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getSubclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); + assertThat(scanResult.getSubclasses(Accepted.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); assertThat(scanResult.getAnnotationsOnClass(AcceptedInterface.class.getName()).getNames()).isEmpty(); } } @@ -299,9 +299,9 @@ public void testRejectedPackage() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(ROOT_PACKAGE, "-" + RejectedSuperclass.class.getPackage().getName()).scan()) { assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getSubclasses(Accepted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()).isEmpty(); + assertThat(scanResult.getSubclasses(Accepted.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) .isEmpty(); } @@ -337,11 +337,11 @@ public void testVisibleIfNotRejected() { try (ScanResult scanResult = new ClassGraph().acceptPackages(ROOT_PACKAGE).enableAnnotationInfo().scan()) { assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()) .containsExactly(RejectedSuperclass.class.getName()); - assertThat(scanResult.getSubclasses(Accepted.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(Accepted.class).getNames()) .containsExactly(RejectedSubclass.class.getName()); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()) .containsExactly(RejectedSubinterface.class.getName()); - assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()) .containsExactly(RejectedSubinterface.class.getName()); assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) .containsExactly(Accepted.class.getName()); diff --git a/src/test/java/io/github/classgraph/test/ClassInfoTest.java b/src/test/java/io/github/classgraph/test/ClassInfoTest.java index 52ddc8a6a..2758b2503 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -104,7 +104,7 @@ public boolean accept(final ClassInfo ci) { */ @Test public void implementsInterfaceDirect() { - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).directOnly().getNames()) + assertThat(scanResult.getClassesImplementing(Iface.class).directOnly().getNames()) .containsOnly(IfaceSub.class.getName(), Impl2.class.getName()); } @@ -113,7 +113,7 @@ public void implementsInterfaceDirect() { */ @Test public void implementsInterface() { - assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()).containsOnly( + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()).containsOnly( Impl1.class.getName(), Impl1Sub.class.getName(), Impl1SubSub.class.getName(), Impl2.class.getName(), Impl2Sub.class.getName(), Impl2SubSub.class.getName(), IfaceSub.class.getName(), IfaceSubSub.class.getName()); diff --git a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java index d5dacdf7a..b70710262 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -55,10 +55,10 @@ public void testAcceptingExternalClassesWithoutEnablingExternalClasses() { assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( InternalExternalTest.class.getName(), InternalExtendsExternal.class.getName(), InternalImplementsExternal.class.getName(), InternalAnnotatedByExternal.class.getName()); - assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(ExternalSuperclass.class).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); assertThat(scanResult.getAllInterfaces()).isEmpty(); - assertThat(scanResult.getClassesImplementing(ExternalInterface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(ExternalInterface.class).getNames()) .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()).isEmpty(); assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class).getNames()) @@ -75,10 +75,10 @@ public void testIncludeReferencedClasses() { .acceptPackages(InternalExternalTest.class.getPackage().getName()).enableAllInfo().scan()) { assertThat(scanResult.getAllStandardClasses().getNames()) .doesNotContain(ExternalSuperclass.class.getName()); - assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) + assertThat(scanResult.getSubclasses(ExternalSuperclass.class).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); assertThat(scanResult.getAllInterfaces().getNames()).doesNotContain(ExternalInterface.class.getName()); - assertThat(scanResult.getClassesImplementing(ExternalInterface.class.getName()).getNames()) + assertThat(scanResult.getClassesImplementing(ExternalInterface.class).getNames()) .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()) .doesNotContain(ExternalAnnotation.class.getName()); From a23818d1168a549515b0644b505fa4464da439a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 17:14:07 -0600 Subject: [PATCH 080/713] Source > Cleanup --- .../github/classgraph/AnnotationInfoList.java | 3 +- .../java/io/github/classgraph/ClassInfo.java | 60 +++++++++++-------- .../java/io/github/classgraph/FieldInfo.java | 15 ++--- .../java/io/github/classgraph/MethodInfo.java | 24 ++++---- .../classgraph/MethodParameterInfo.java | 24 ++++---- .../java/io/github/classgraph/ModuleInfo.java | 10 ++-- .../io/github/classgraph/PackageInfo.java | 7 ++- .../java/io/github/classgraph/ScanResult.java | 33 ++++++---- .../io/github/classgraph/utils/Assert.java | 20 ++++++- .../issues/issue101/Issue101Test.java | 11 ++-- .../classgraph/issues/issue318/Issue318.java | 4 +- .../classgraph/test/ClassGraphTest.java | 9 +-- .../github/classgraph/test/ClassInfoTest.java | 4 +- .../AnnotationClassRefTest.java | 3 +- .../FieldAndMethodAnnotationTest.java | 12 ++-- .../MethodAnnotationTest.java | 4 +- .../TestMethodMetaAnnotation.java | 7 +-- 17 files changed, 143 insertions(+), 107 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index 0bc31c421..79a0151d7 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -349,7 +349,8 @@ public AnnotationInfoList directOnly() { /** * Get the {@link Repeatable} annotation with the given class, or the empty list if none found. * - * @param annotationClass The class to search for. + * @param annotationClass + * The class to search for. * @return The list of annotations with the given class, or the empty list if none found. */ public AnnotationInfoList getRepeatable(final Class annotationClass) { diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index fc6c7ae53..92d72d986 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1296,7 +1296,8 @@ public boolean isArrayClass() { /** * Checks if this class extends the superclass. * - * @param superclass A superclass. + * @param superclass + * A superclass. * @return true if this class extends the superclass. */ public boolean extendsSuperclass(final Class superclass) { @@ -1363,7 +1364,8 @@ public boolean isImplementedInterface() { /** * Checks whether this class implements the interface. * - * @param interfaceClazz An interface. + * @param interfaceClazz + * An interface. * @return true if this class implements the interface. */ public boolean implementsInterface(final Class interfaceClazz) { @@ -1385,7 +1387,8 @@ public boolean implementsInterface(final String interfaceName) { /** * Checks whether this class has the annotation. * - * @param annotation An annotation. + * @param annotation + * An annotation. * @return true if this class has the annotation. */ public boolean hasAnnotation(final Class annotation) { @@ -1434,7 +1437,8 @@ public boolean hasField(final String fieldName) { /** * Checks whether this class declares a field with the annotation. * - * @param annotation A field annotation. + * @param annotation + * A field annotation. * @return true if this class declares a field with the annotation. */ public boolean hasDeclaredFieldAnnotation(final Class annotation) { @@ -1461,7 +1465,8 @@ public boolean hasDeclaredFieldAnnotation(final String fieldAnnotationName) { /** * Checks whether this class or one of its superclasses declares a field with the annotation. * - * @param fieldAnnotation A field annotation. + * @param fieldAnnotation + * A field annotation. * @return true if this class or one of its superclasses declares a field with the annotation. */ public boolean hasFieldAnnotation(final Class fieldAnnotation) { @@ -1515,7 +1520,8 @@ public boolean hasMethod(final String methodName) { /** * Checks whether this class declares a method with the annotation. * - * @param methodAnnotation A method annotation. + * @param methodAnnotation + * A method annotation. * @return true if this class declares a method with the annotation. */ public boolean hasDeclaredMethodAnnotation(final Class methodAnnotation) { @@ -1540,12 +1546,11 @@ public boolean hasDeclaredMethodAnnotation(final String methodAnnotationName) { } /** - * Checks whether this class or one of its superclasses or interfaces declares a method with the - * annotation. + * Checks whether this class or one of its superclasses or interfaces declares a method with the annotation. * - * @param methodAnnotation A method annotation. - * @return true if this class or one of its superclasses or interfaces declares a method with the - * annotation. + * @param methodAnnotation + * A method annotation. + * @return true if this class or one of its superclasses or interfaces declares a method with the annotation. */ public boolean hasMethodAnnotation(final Class methodAnnotation) { Assert.isAnnotation(methodAnnotation); @@ -1573,10 +1578,12 @@ public boolean hasMethodAnnotation(final String methodAnnotationName) { /** * Checks whether this class declares a method with the annotation. * - * @param methodParameterAnnotation A method annotation. + * @param methodParameterAnnotation + * A method annotation. * @return true if this class declares a method with the annotation. */ - public boolean hasDeclaredMethodParameterAnnotation(final Class methodParameterAnnotation) { + public boolean hasDeclaredMethodParameterAnnotation( + final Class methodParameterAnnotation) { Assert.isAnnotation(methodParameterAnnotation); return hasDeclaredMethodParameterAnnotation(methodParameterAnnotation.getName()); } @@ -1600,7 +1607,8 @@ public boolean hasDeclaredMethodParameterAnnotation(final String methodParameter /** * Checks whether this class or one of its superclasses or interfaces has a method with the annotation. * - * @param methodParameterAnnotation A method annotation. + * @param methodParameterAnnotation + * A method annotation. * @return true if this class or one of its superclasses or interfaces has a method with the annotation. */ public boolean hasMethodParameterAnnotation(final Class methodParameterAnnotation) { @@ -1939,22 +1947,22 @@ public AnnotationInfoList getAnnotationInfo() { } /** - * Get a the non-{@link Repeatable} annotation on this class, or null if the class does not have the - * annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) + * Get a the non-{@link Repeatable} annotation on this class, or null if the class does not have the annotation. + * (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) * *

* Also handles the {@link Inherited} meta-annotation, which causes an annotation to annotate a class and all of * its subclasses. * *

- * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, - * and then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list - * doesn't have to be built multiple times. + * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, and + * then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list doesn't have + * to be built multiple times. * * @param annotation * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this class, or null if the - * class does not have the annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this class, or null if the class does + * not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -1992,14 +2000,14 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { * its subclasses. * *

- * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, - * and then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list - * doesn't have to be built multiple times. + * Note that if you need to get multiple annotations, it is faster to call {@link #getAnnotationInfo()}, and + * then get the annotations from the returned {@link AnnotationInfoList}, so that the returned list doesn't have + * to be built multiple times. * * @param annotation * The annotation. - * @return An {@link AnnotationInfoList} of all instances of the annotation on this class, or the empty - * list if the class does not have the annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this class, or the empty list if + * the class does not have the annotation. */ public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { Assert.isAnnotation(annotation); diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index a6f960181..dc6f339cb 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -373,13 +373,13 @@ public AnnotationInfoList getAnnotationInfo() { } /** - * Get a the non-{@link Repeatable} annotation on this field, or null if the field does not have the - * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) + * Get a the non-{@link Repeatable} annotation on this field, or null if the field does not have the annotation. + * (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) * * @param annotation * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this field, or null if the - * field does not have the annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this field, or null if the field does + * not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -405,8 +405,8 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { * * @param annotation * The annotation. - * @return An {@link AnnotationInfoList} of all instances of the annotation on this field, or the empty - * list if the field does not have the annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this field, or the empty list if + * the field does not have the annotation. */ public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { Assert.isAnnotation(annotation); @@ -429,7 +429,8 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam /** * Check if the field has a given annotation. * - * @param annotation The annotation. + * @param annotation + * The annotation. * @return true if this field has the annotation. */ public boolean hasAnnotation(final Class annotation) { diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 754d3d3f6..d42da3da9 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -578,9 +578,10 @@ public AnnotationInfoList getAnnotationInfo() { * Get a the non-{@link Repeatable} annotation on this method, or null if the method does not have the * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) * - * @param annotation The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this method, or null if the - * method does not have the annotation. + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this method, or null if the method + * does not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -601,12 +602,13 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { } /** - * Get a the {@link Repeatable} annotation on this method, or the empty list if the method does not have - * the annotation. + * Get a the {@link Repeatable} annotation on this method, or the empty list if the method does not have the + * annotation. * - * @param annotation The annotation. - * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method, or the - * empty list if the method does not have the annotation. + * @param annotation + * The annotation. + * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method, or the empty + * list if the method does not have the annotation. */ public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { Assert.isAnnotation(annotation); @@ -629,7 +631,8 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam /** * Check if this method has the annotation. * - * @param annotation The annotation. + * @param annotation + * The annotation. * @return true if this method has the annotation. */ public boolean hasAnnotation(final Class annotation) { @@ -651,7 +654,8 @@ public boolean hasAnnotation(final String annotationName) { /** * Check if this method has a parameter with the annotation. * - * @param annotation The method parameter annotation. + * @param annotation + * The method parameter annotation. * @return true if this method has a parameter with the annotation. */ public boolean hasParameterAnnotation(final Class annotation) { diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index 652397f77..090e99208 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -28,8 +28,6 @@ */ package io.github.classgraph; -import nonapi.io.github.classgraph.utils.Assert; - import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.Modifier; @@ -37,6 +35,8 @@ import java.util.Collections; import java.util.Objects; +import nonapi.io.github.classgraph.utils.Assert; + /** * Information on the parameters of a method. * @@ -182,14 +182,13 @@ public AnnotationInfoList getAnnotationInfo() { } /** - * Get a the non-{@link Repeatable} annotation on this method, or null if the method parameter does not - * have the annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} - * annotations.) + * Get a the non-{@link Repeatable} annotation on this method, or null if the method parameter does not have the + * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) * * @param annotation * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this method parameter, or null - * if the method parameter does not have the annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this method parameter, or null if the + * method parameter does not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -211,13 +210,13 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { } /** - * Get a the {@link Repeatable} annotation on this method, or the empty list if the method parameter does - * not have the annotation. + * Get a the {@link Repeatable} annotation on this method, or the empty list if the method parameter does not + * have the annotation. * * @param annotation * The annotation. - * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method - * parameter, or the empty list if the method parameter does not have the annotation. + * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method parameter, or + * the empty list if the method parameter does not have the annotation. */ public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { Assert.isAnnotation(annotation); @@ -240,7 +239,8 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationNam /** * Check whether this method parameter has the annotation. * - * @param annotation The annotation. + * @param annotation + * The annotation. * @return true if this method parameter has the annotation. */ public boolean hasAnnotation(final Class annotation) { diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 8b94c725b..57b5801e8 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -236,9 +236,10 @@ void addAnnotations(final AnnotationInfoList moduleAnnotations) { /** * Get a the annotation on this module, or null if the module does not have the annotation. * - * @param annotation The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this module, or null if the - * module does not have the annotation. + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this module, or null if the module + * does not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -277,7 +278,8 @@ public AnnotationInfoList getAnnotationInfo() { /** * Check if this module has the annotation. * - * @param annotation The annotation. + * @param annotation + * The annotation. * @return true if this module has the annotation. */ public boolean hasAnnotation(final Class annotation) { diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 7d180720c..6d10566c6 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -129,8 +129,8 @@ void addClassInfo(final ClassInfo classInfo) { * * @param annotation * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this package, or null if the - * package does not have the annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this package, or null if the package + * does not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -169,7 +169,8 @@ public AnnotationInfoList getAnnotationInfo() { /** * Check if the package has the annotation. * - * @param annotation The annotation. + * @param annotation + * The annotation. * @return true if this package has the annotation. */ public boolean hasAnnotation(final Class annotation) { diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 1a34e24cb..613af2b41 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -57,7 +57,11 @@ import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.utils.*; +import nonapi.io.github.classgraph.utils.Assert; +import nonapi.io.github.classgraph.utils.CollectionUtils; +import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.JarUtils; +import nonapi.io.github.classgraph.utils.LogNode; /** * The result of a scan. You should assign a ScanResult in a try-with-resources block, or manually close it when you @@ -883,7 +887,8 @@ public ClassInfoList getAllStandardClasses() { /** * Get all subclasses of the superclass. * - * @param superclass The superclass. + * @param superclass + * The superclass. * @return A list of subclasses of the superclass, or the empty list if none. */ public ClassInfoList getSubclasses(final Class superclass) { @@ -934,7 +939,8 @@ public ClassInfoList getSuperclasses(final String subclassName) { /** * Get classes that have a method with an annotation of the named type. * - * @param methodAnnotation the method annotation. + * @param methodAnnotation + * the method annotation. * @return A list of classes with a method that has an annotation of the named type, or the empty list if none. */ public ClassInfoList getClassesWithMethodAnnotation(final Class methodAnnotation) { @@ -964,11 +970,13 @@ public ClassInfoList getClassesWithMethodAnnotation(final String methodAnnotatio /** * Get classes that have a method with a parameter that is annotated with an annotation of the named type. * - * @param methodParameterAnnotation the method parameter annotation. + * @param methodParameterAnnotation + * the method parameter annotation. * @return A list of classes that have a method with a parameter annotated with the named annotation type, or - * the empty list if none. + * the empty list if none. */ - public ClassInfoList getClassesWithMethodParameterAnnotation(final Class methodParameterAnnotation) { + public ClassInfoList getClassesWithMethodParameterAnnotation( + final Class methodParameterAnnotation) { Assert.isAnnotation(methodParameterAnnotation); return getClassesWithMethodParameterAnnotation(methodParameterAnnotation.getName()); } @@ -996,7 +1004,8 @@ public ClassInfoList getClassesWithMethodParameterAnnotation(final String method /** * Get classes that have a field with an annotation of the named type. * - * @param fieldAnnotation the field annotation. + * @param fieldAnnotation + * the field annotation. * @return A list of classes that have a field with an annotation of the named type, or the empty list if none. */ public ClassInfoList getClassesWithFieldAnnotation(final Class fieldAnnotation) { @@ -1066,7 +1075,8 @@ public ClassInfoList getInterfaces(final String className) { * Get all classes that implement (or have superclasses that implement) the interface (or one of its * subinterfaces). * - * @param interfaceClass The interface class. + * @param interfaceClass + * The interface class. * @return A list of all classes that implement the interface, or the empty list if none. */ public ClassInfoList getClassesImplementing(final Class interfaceClass) { @@ -1132,9 +1142,10 @@ public ClassInfoList getAllInterfacesAndAnnotations() { /** * Get classes with the class annotation or meta-annotation. * - * @param annotation The class annotation or meta-annotation. - * @return A list of all non-annotation classes that were found with the class annotation during the scan, - * or the empty list if none. + * @param annotation + * The class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the class annotation during the scan, or + * the empty list if none. */ public ClassInfoList getClassesWithAnnotation(final Class annotation) { Assert.isAnnotation(annotation); diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java index 204eaa38f..8ca51d781 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java @@ -1,14 +1,28 @@ package nonapi.io.github.classgraph.utils; +/** Assertions. */ public final class Assert { - - public static void isAnnotation(Class clazz) { + /** + * Throw {@link IllegalArgumentException} if the class is not an annotation. + * + * @param clazz + * the class. + * @throw {@link IllegalArgumentException} if the class is not an annotation. + */ + public static void isAnnotation(final Class clazz) { if (!clazz.isAnnotation()) { throw new IllegalArgumentException(clazz + " is not an annotation"); } } - public static void isInterface(Class clazz) { + /** + * Throw {@link IllegalArgumentException} if the class is not an interface. + * + * @param clazz + * the class. + * @throw {@link IllegalArgumentException} if the class is not an interface. + */ + public static void isInterface(final Class clazz) { if (!clazz.isInterface()) { throw new IllegalArgumentException(clazz + " is not an interface"); } diff --git a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java index 645406af9..1d60e27c1 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -58,9 +58,8 @@ public void nonInheritedAnnotation() { public void inheritedMetaAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class) - .getStandardClasses().getNames()).containsOnly(AnnotatedClass.class.getName(), - NonAnnotatedSubclass.class.getName()); + assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class).getStandardClasses() + .getNames()).containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName()); } } @@ -71,9 +70,9 @@ public void inheritedMetaAnnotation() { public void inheritedAnnotation() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class).getNames()) - .containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName(), - AnnotatedInterface.class.getName()); + assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class).getNames()).containsOnly( + AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName(), + AnnotatedInterface.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java index b182f27cf..ae356e26d 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -81,8 +81,8 @@ public void issue318() { .scan()) { assertThat(scanResult.getClassesWithAnnotation(MyAnn.class).getNames()).containsOnly( With1MyAnn.class.getName(), With2MyAnn.class.getName(), With3MyAnn.class.getName()); - assertThat(scanResult.getClassInfo(With3MyAnn.class.getName()) - .getAnnotationInfoRepeatable(MyAnn.class).size()).isEqualTo(3); + assertThat(scanResult.getClassInfo(With3MyAnn.class.getName()).getAnnotationInfoRepeatable(MyAnn.class) + .size()).isEqualTo(3); } } } diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 94a0a412d..eeeed4ab0 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -164,8 +164,7 @@ public void scanTransitiveImplements() { assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .doesNotContain(Cls.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) - .contains(Impl1.class.getName()); + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()).contains(Impl1.class.getName()); assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .contains(Impl1.class.getName()); assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) @@ -183,8 +182,7 @@ public void scanTransitiveImplements() { assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) .contains(Impl1SubSub.class.getName()); - assertThat(scanResult.getClassesImplementing(Iface.class).getNames()) - .contains(Impl2.class.getName()); + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()).contains(Impl2.class.getName()); assertThat(scanResult.getClassesImplementing(IfaceSub.class).getNames()) .doesNotContain(Impl2.class.getName()); assertThat(scanResult.getClassesImplementing(IfaceSubSub.class).getNames()) @@ -302,8 +300,7 @@ public void testRejectedPackage() { assertThat(scanResult.getSubclasses(Accepted.class).getNames()).isEmpty(); assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); assertThat(scanResult.getClassesImplementing(AcceptedInterface.class).getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()) - .isEmpty(); + assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class).getNames()).isEmpty(); } } diff --git a/src/test/java/io/github/classgraph/test/ClassInfoTest.java b/src/test/java/io/github/classgraph/test/ClassInfoTest.java index 2758b2503..546db809f 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -113,8 +113,8 @@ public void implementsInterfaceDirect() { */ @Test public void implementsInterface() { - assertThat(scanResult.getClassesImplementing(Iface.class).getNames()).containsOnly( - Impl1.class.getName(), Impl1Sub.class.getName(), Impl1SubSub.class.getName(), Impl2.class.getName(), + assertThat(scanResult.getClassesImplementing(Iface.class).getNames()).containsOnly(Impl1.class.getName(), + Impl1Sub.class.getName(), Impl1SubSub.class.getName(), Impl2.class.getName(), Impl2Sub.class.getName(), Impl2SubSub.class.getName(), IfaceSub.class.getName(), IfaceSubSub.class.getName()); } diff --git a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java index 1153a5154..076118a3b 100644 --- a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java +++ b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java @@ -85,8 +85,7 @@ public void testClassRefAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(AnnotationClassRefTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { - final ClassInfoList testClasses = scanResult - .getClassesWithMethodAnnotation(ClassRefAnnotation.class); + final ClassInfoList testClasses = scanResult.getClassesWithMethodAnnotation(ClassRefAnnotation.class); assertThat(testClasses.size()).isEqualTo(1); final ClassInfo testClass = testClasses.get(0); final MethodInfo method = testClass.getMethodInfo().getSingleMethod("methodWithAnnotation"); diff --git a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java index 6b7550b14..2c535d1cb 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -60,8 +60,8 @@ public void testGetNamesOfClassesWithFieldAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .enableAnnotationInfo().scan()) { - final List testClasses = scanResult - .getClassesWithFieldAnnotation(ExternalAnnotation.class).getNames(); + final List testClasses = scanResult.getClassesWithFieldAnnotation(ExternalAnnotation.class) + .getNames(); assertThat(testClasses).isEmpty(); } } @@ -74,8 +74,8 @@ public void testGetNamesOfClassesWithFieldAnnotationIgnoringVisibility() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .ignoreFieldVisibility().enableAnnotationInfo().scan()) { - final List testClasses = scanResult - .getClassesWithFieldAnnotation(ExternalAnnotation.class).getNames(); + final List testClasses = scanResult.getClassesWithFieldAnnotation(ExternalAnnotation.class) + .getNames(); assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } @@ -89,8 +89,8 @@ public void testGetNamesOfClassesWithMethodAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { - final List testClasses = scanResult - .getClassesWithMethodAnnotation(ExternalAnnotation.class).getNames(); + final List testClasses = scanResult.getClassesWithMethodAnnotation(ExternalAnnotation.class) + .getNames(); assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java index 0148dbaf6..69c88195b 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -53,8 +53,8 @@ public void testGetNamesOfClassesWithMethodAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() .enableMethodInfo().enableAnnotationInfo().scan()) { - final List testClasses = scanResult - .getClassesWithMethodAnnotation(ExternalAnnotation.class).getNames(); + final List testClasses = scanResult.getClassesWithMethodAnnotation(ExternalAnnotation.class) + .getNames(); assertThat(testClasses).isEmpty(); } } diff --git a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java index 03f839691..228cffd25 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -107,8 +107,7 @@ public void testMetaAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() .scan()) { - final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class) - .getNames(); + final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class).getNames(); assertThat(testClasses).containsOnly(MethodAnnotation.class.getName(), ClassAnnotation.class.getName(), MetaAnnotatedClass.class.getName()); } @@ -138,8 +137,8 @@ public void testMethodMetaAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { - final List testClasses = scanResult - .getClassesWithMethodAnnotation(MetaAnnotation.class).getNames(); + final List testClasses = scanResult.getClassesWithMethodAnnotation(MetaAnnotation.class) + .getNames(); assertThat(testClasses).containsOnly(ClassWithMetaAnnotatedMethod.class.getName()); } } From cab618baff2ace68af085eeb5ba559442df882dd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 17:14:48 -0600 Subject: [PATCH 081/713] [maven-release-plugin] prepare release classgraph-4.8.115 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4367b14fa..b5bcfd844 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.115-SNAPSHOT + 4.8.115 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.115 From 9d393db2ec99e6a7622a3587a8883b673e47df9c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 17:14:50 -0600 Subject: [PATCH 082/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b5bcfd844..0457dc958 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.115 + 4.8.116-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.115 + classgraph-4.8.105 From 84eb3d4915d371e199462e98b7033b9ed0b7432e Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:29:13 +0200 Subject: [PATCH 083/713] Unnecessary call to 'toString()' --- src/main/java/io/github/classgraph/AnnotationEnumValue.java | 6 +++--- .../java/io/github/classgraph/AnnotationParameterValue.java | 2 +- .../java/io/github/classgraph/ClasspathElementFileDir.java | 2 +- .../java/io/github/classgraph/ClasspathElementModule.java | 2 +- .../java/io/github/classgraph/ClasspathElementPathDir.java | 2 +- .../classloaderhandler/EquinoxClassLoaderHandler.java | 2 +- .../nonapi/io/github/classgraph/json/JSONSerializer.java | 2 +- .../io/github/classgraph/json/ParameterizedTypeImpl.java | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 104526630..4b855a260 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -118,15 +118,15 @@ public Object loadClassAndReturnEnumValue(final boolean ignoreExceptions) throws try { field = classRef.getDeclaredField(valueName); } catch (final ReflectiveOperationException | SecurityException e) { - throw new IllegalArgumentException("Could not find enum constant " + toString(), e); + throw new IllegalArgumentException("Could not find enum constant " + this, e); } if (!field.isEnumConstant()) { - throw new IllegalArgumentException("Field " + toString() + " is not an enum constant"); + throw new IllegalArgumentException("Field " + this + " is not an enum constant"); } try { return field.get(null); } catch (final ReflectiveOperationException | SecurityException e) { - throw new IllegalArgumentException("Field " + toString() + " is not accessible", e); + throw new IllegalArgumentException("Field " + this + " is not accessible", e); } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index ddbbec91d..caa3db213 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -252,7 +252,7 @@ private static void toString(final Object val, final boolean useSimpleNames, fin } else if (val instanceof ScanResultObject) { ((ScanResultObject) val).toString(useSimpleNames, buf); } else { - buf.append(val.toString()); + buf.append(val); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 9e4de125d..b2db44e0d 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -495,7 +495,7 @@ void scanPaths(final LogNode log) { } if (scanned.getAndSet(true)) { // Should not happen - throw new IllegalArgumentException("Already scanned classpath element " + toString()); + throw new IllegalArgumentException("Already scanned classpath element " + this); } final LogNode subLog = log == null ? null diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 15af59210..80270b169 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -269,7 +269,7 @@ void scanPaths(final LogNode log) { } if (scanned.getAndSet(true)) { // Should not happen - throw new IllegalArgumentException("Already scanned classpath element " + toString()); + throw new IllegalArgumentException("Already scanned classpath element " + this); } final LogNode subLog = log == null ? null diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index c90f4ca87..c46ec080c 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -526,7 +526,7 @@ void scanPaths(final LogNode log) { } if (scanned.getAndSet(true)) { // Should not happen - throw new IllegalArgumentException("Already scanned classpath element " + toString()); + throw new IllegalArgumentException("Already scanned classpath element " + this); } final LogNode subLog = log == null ? null diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java index 5517fd6b4..3a1159e76 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -127,7 +127,7 @@ private static void addBundleFile(final Object bundlefile, final Set pat sep = "!/"; } } - final String pathElement = base.toString() + sep + fieldVal.toString(); + final String pathElement = base + sep + fieldVal; classpathOrderOut.addClasspathEntry(pathElement, classLoader, scanSpec, log); break; } diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java index 4b8d53b34..7a27257d3 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -456,7 +456,7 @@ static void jsonValToJSONString(final Object jsonVal, } else { // Serialize a numeric or Boolean type (Integer, Long, Short, Float, Double, Boolean, Byte) to string // (doesn't need quoting or escaping) - buf.append(jsonVal.toString()); + buf.append(jsonVal); } } diff --git a/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java b/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java index 26c76fcdb..9419ad082 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java @@ -133,7 +133,7 @@ public String toString() { if (ownerType instanceof Class) { buf.append(((Class) ownerType).getName()); } else { - buf.append(ownerType.toString()); + buf.append(ownerType); } buf.append('$'); if (ownerType instanceof ParameterizedTypeImpl) { From 59d6fc6c9aca5b2eb06150b66dacb1082f30992e Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:29:59 +0200 Subject: [PATCH 084/713] Unnecessary conversion to String --- .../classgraph/ObjectTypedValueWrapper.java | 16 ++++++++-------- .../io/github/classgraph/json/JSONUtils.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index a18f175e0..d0b48cf31 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -629,21 +629,21 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { } else if (stringValue != null) { buf.append(stringValue); } else if (integerValue != null) { - buf.append(Integer.toString(integerValue)); + buf.append(integerValue); } else if (longValue != null) { - buf.append(Long.toString(longValue)); + buf.append(longValue); } else if (shortValue != null) { - buf.append(Short.toString(shortValue)); + buf.append(shortValue); } else if (booleanValue != null) { - buf.append(Boolean.toString(booleanValue)); + buf.append(booleanValue); } else if (characterValue != null) { - buf.append(Character.toString(characterValue)); + buf.append(characterValue); } else if (floatValue != null) { - buf.append(Float.toString(floatValue)); + buf.append(floatValue); } else if (doubleValue != null) { - buf.append(Double.toString(doubleValue)); + buf.append(doubleValue); } else if (byteValue != null) { - buf.append(Byte.toString(byteValue)); + buf.append(byteValue); } else if (stringArrayValue != null) { buf.append(Arrays.toString(stringArrayValue)); } else if (intArrayValue != null) { diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java index d6afa882e..4f880cc3f 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -76,7 +76,7 @@ public final class JSONUtils { final char hexDigit1 = nibble1 <= 9 ? (char) ('0' + nibble1) : (char) ('A' + nibble1 - 10); final int nibble0 = c & 0xf; final char hexDigit0 = nibble0 <= 9 ? (char) ('0' + nibble0) : (char) ('A' + nibble0 - 10); - JSON_CHAR_REPLACEMENTS[c] = "\\u00" + Character.toString(hexDigit1) + Character.toString(hexDigit0); + JSON_CHAR_REPLACEMENTS[c] = "\\u00" + hexDigit1 + hexDigit0; } JSON_CHAR_REPLACEMENTS['"'] = "\\\""; JSON_CHAR_REPLACEMENTS['\\'] = "\\\\"; From a23bec2c559be279237988e2b54119995e5a2877 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:30:29 +0200 Subject: [PATCH 085/713] Unnecessary interface modifier --- .../java/io/github/classgraph/Classfile.java | 6 ++--- .../java/io/github/classgraph/HasName.java | 2 +- .../fileslice/reader/RandomAccessReader.java | 24 +++++++++---------- .../fileslice/reader/SequentialReader.java | 20 ++++++++-------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 736a3b868..ab39e6fa0 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1003,15 +1003,15 @@ private Object readAnnotationElementValue() throws IOException { // ------------------------------------------------------------------------------------------------------------- - static interface ClassTypeAnnotationDecorator { + interface ClassTypeAnnotationDecorator { void decorate(ClassTypeSignature classTypeSignature); } - static interface MethodTypeAnnotationDecorator { + interface MethodTypeAnnotationDecorator { void decorate(MethodTypeSignature methodTypeSignature); } - static interface TypeAnnotationDecorator { + interface TypeAnnotationDecorator { void decorate(TypeSignature typeSignature); } diff --git a/src/main/java/io/github/classgraph/HasName.java b/src/main/java/io/github/classgraph/HasName.java index 7080ca7e2..5ac3794f0 100644 --- a/src/main/java/io/github/classgraph/HasName.java +++ b/src/main/java/io/github/classgraph/HasName.java @@ -35,5 +35,5 @@ public interface HasName { * * @return The name. */ - public String getName(); + String getName(); } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java index 4d9f770fa..8842afa25 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java @@ -48,7 +48,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public int read(long srcOffset, ByteBuffer dstBuf, int dstBufStart, int numBytes) throws IOException; + int read(long srcOffset, ByteBuffer dstBuf, int dstBufStart, int numBytes) throws IOException; /** * Read bytes into a byte array. @@ -65,7 +65,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public int read(long srcOffset, byte[] dstArr, int dstArrStart, int numBytes) throws IOException; + int read(long srcOffset, byte[] dstArr, int dstArrStart, int numBytes) throws IOException; /** * Read a byte at a specific offset (without changing the current cursor offset). @@ -76,7 +76,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public byte readByte(final long offset) throws IOException; + byte readByte(final long offset) throws IOException; /** * Read an unsigned byte at a specific offset (without changing the current cursor offset). @@ -87,7 +87,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public int readUnsignedByte(final long offset) throws IOException; + int readUnsignedByte(final long offset) throws IOException; /** * Read a short at a specific offset (without changing the current cursor offset). @@ -98,7 +98,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public short readShort(final long offset) throws IOException; + short readShort(final long offset) throws IOException; /** * Read a unsigned short at a specific offset (without changing the current cursor offset). @@ -109,7 +109,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public int readUnsignedShort(final long offset) throws IOException; + int readUnsignedShort(final long offset) throws IOException; /** * Read a int at a specific offset (without changing the current cursor offset). @@ -120,7 +120,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public int readInt(final long offset) throws IOException; + int readInt(final long offset) throws IOException; /** * Read a unsigned int at a specific offset (without changing the current cursor offset). @@ -131,7 +131,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public long readUnsignedInt(final long offset) throws IOException; + long readUnsignedInt(final long offset) throws IOException; /** * Read a long at a specific offset (without changing the current cursor offset). @@ -142,7 +142,7 @@ public interface RandomAccessReader { * @throws IOException * If there was an exception while reading. */ - public long readLong(final long offset) throws IOException; + long readLong(final long offset) throws IOException; /** * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and @@ -160,8 +160,8 @@ public interface RandomAccessReader { * @throws IOException * If an I/O exception occurs. */ - public String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, - final boolean stripLSemicolon) throws IOException; + String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, + final boolean stripLSemicolon) throws IOException; /** * Reads the "modified UTF8" format defined in the Java classfile spec. @@ -174,5 +174,5 @@ public String readString(final long offset, final int numBytes, final boolean re * @throws IOException * If an I/O exception occurs. */ - public String readString(final long offset, final int numBytes) throws IOException; + String readString(final long offset, final int numBytes) throws IOException; } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java index 5cf4b6835..a86ad8c79 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java @@ -39,7 +39,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public byte readByte() throws IOException; + byte readByte() throws IOException; /** * Read an unsigned byte at the current cursor position. @@ -48,7 +48,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public int readUnsignedByte() throws IOException; + int readUnsignedByte() throws IOException; /** * Read a short at the current cursor position. @@ -57,7 +57,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public short readShort() throws IOException; + short readShort() throws IOException; /** * Read a unsigned short at the current cursor position. @@ -66,7 +66,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public int readUnsignedShort() throws IOException; + int readUnsignedShort() throws IOException; /** * Read a int at the current cursor position. @@ -75,7 +75,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public int readInt() throws IOException; + int readInt() throws IOException; /** * Read a unsigned int at the current cursor position. @@ -84,7 +84,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public long readUnsignedInt() throws IOException; + long readUnsignedInt() throws IOException; /** * Read a long at the current cursor position. @@ -93,7 +93,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public long readLong() throws IOException; + long readLong() throws IOException; /** * Skip the given number of bytes. @@ -103,7 +103,7 @@ public interface SequentialReader { * @throws IOException * If there was an exception while reading. */ - public void skip(final int bytesToSkip) throws IOException; + void skip(final int bytesToSkip) throws IOException; /** * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and @@ -119,7 +119,7 @@ public interface SequentialReader { * @throws IOException * If an I/O exception occurs. */ - public String readString(final int numBytes, final boolean replaceSlashWithDot, final boolean stripLSemicolon) + String readString(final int numBytes, final boolean replaceSlashWithDot, final boolean stripLSemicolon) throws IOException; /** @@ -131,5 +131,5 @@ public String readString(final int numBytes, final boolean replaceSlashWithDot, * @throws IOException * If an I/O exception occurs. */ - public String readString(final int numBytes) throws IOException; + String readString(final int numBytes) throws IOException; } From 74967fbd66bf0dd763e8c7d334ce1c6906648df0 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:31:01 +0200 Subject: [PATCH 086/713] Unnecessary semicolon --- .../java/nonapi/io/github/classgraph/json/FieldTypeInfo.java | 2 +- src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java b/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java index f511d66c7..c4de8f3fe 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java +++ b/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java @@ -98,7 +98,7 @@ private enum PrimitiveType { /** Character type. */ CHARACTER, /** Class reference */ - CLASS_REF; + CLASS_REF } /** diff --git a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java index afb56c92f..bb8385303 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -80,7 +80,7 @@ public enum ModifierType { /** The modifier bits apply to a method. */ METHOD, /** The modifier bits apply to a field. */ - FIELD; + FIELD } /** From 39c9a1db43e9b15281764ea9849acafd09c97f68 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:32:05 +0200 Subject: [PATCH 087/713] Redundant local variable --- src/main/java/io/github/classgraph/ClassTypeSignature.java | 3 +-- src/main/java/io/github/classgraph/ModuleRef.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 89a150bc5..2e9b77b2b 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -415,8 +415,7 @@ static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo cla if (parser.hasMore()) { throw new ParseException(parser, "Extra characters at end of type descriptor"); } - final ClassTypeSignature classTypeSignature = new ClassTypeSignature(classInfo, typeParameters, + return new ClassTypeSignature(classInfo, typeParameters, superclassSignature, superinterfaceSignatures, throwsSignatures); - return classTypeSignature; } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 7edee00eb..23ebe44c1 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -93,9 +93,8 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { // Should not happen throw new IllegalArgumentException("moduleReference.descriptor() should not return null"); } - final String moduleName = (String) ReflectionUtils.invokeMethod(this.descriptor, "name", + this.name = (String) ReflectionUtils.invokeMethod(this.descriptor, "name", /* throwException = */ true); - this.name = moduleName; @SuppressWarnings("unchecked") final Set modulePackages = (Set) ReflectionUtils.invokeMethod(this.descriptor, "packages", /* throwException = */ true); From 35736f3ca536b92e39fab13b48783653c2135bd1 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:36:13 +0200 Subject: [PATCH 088/713] 'for' loop replaceable with enhanced 'for' loop --- src/main/java/io/github/classgraph/ClassInfo.java | 3 +-- src/main/java/io/github/classgraph/ClassRefTypeSignature.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 92d72d986..8d8d56fa1 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -630,8 +630,7 @@ void addMethodInfo(final MethodInfoList methodInfoList, final Map typePath, final Annota // Find how many deeper nested levels to descend to int numDeeperNestedLevels = 0; int nextTypeArgIdx = -1; - for (int i = 0; i < typePath.size(); i++) { - final TypePathNode typePathNode = typePath.get(i); + for (final TypePathNode typePathNode : typePath) { if (typePathNode.typePathKind == 1) { numDeeperNestedLevels++; } else if (typePathNode.typePathKind == 3) { From 6b6e090033fc5c5e2825dc0529289dded62b15ea Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:37:02 +0200 Subject: [PATCH 089/713] Explicit type can be replaced with <> --- src/main/java/io/github/classgraph/ClassInfoList.java | 2 +- src/main/java/io/github/classgraph/Classfile.java | 4 ++-- src/main/java/io/github/classgraph/ScanResult.java | 2 +- src/main/java/io/github/classgraph/Scanner.java | 2 +- .../io/github/classgraph/classpath/ClassLoaderOrder.java | 2 +- .../io/github/classgraph/concurrency/InterruptionChecker.java | 2 +- .../java/nonapi/io/github/classgraph/recycler/Recycler.java | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 5045baa5a..794af1659 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -170,7 +170,7 @@ public ClassInfoList(final int sizeHint) { public ClassInfoList(final Collection classInfoCollection) { this(classInfoCollection instanceof Set // ? (Set) classInfoCollection - : new HashSet(classInfoCollection), // + : new HashSet<>(classInfoCollection), // /* directlyRelatedClasses = */ null, /* sortByName = */ true); } diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index ab39e6fa0..7fbd5ce8f 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1058,8 +1058,8 @@ private void readConstantPoolEntries() throws IOException { List classNameCpIdxs = null; List typeSignatureIdxs = null; if (scanSpec.enableInterClassDependencies) { - classNameCpIdxs = new ArrayList(); - typeSignatureIdxs = new ArrayList(); + classNameCpIdxs = new ArrayList<>(); + typeSignatureIdxs = new ArrayList<>(); } // Read size of constant pool diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 613af2b41..740101556 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -774,7 +774,7 @@ public Map getReverseClassDependencyMap() { for (final ClassInfo dep : ci.getClassDependencies()) { Set set = revMapSet.get(dep); if (set == null) { - revMapSet.put(dep, set = new HashSet()); + revMapSet.put(dep, set = new HashSet<>()); } set.add(ci); } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 45cca47e0..1857790fe 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -940,7 +940,7 @@ private ScanResult performScan(final List finalClasspathEltOrd if (scanSpec.enableClassInfo) { // Get accepted classfile order final List classfileScanWorkItems = new ArrayList<>(); - final Set acceptedClassNamesFound = new HashSet(); + final Set acceptedClassNamesFound = new HashSet<>(); for (final ClasspathElement classpathElement : finalClasspathEltOrder) { // Get classfile scan order across all classpath elements for (final Resource resource : classpathElement.acceptedClassfileResources) { diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java index a39f70212..517646b87 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -73,7 +73,7 @@ public class ClassLoaderOrder { /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ private final Map classLoaderToClassLoaderHandlerRegistryEntry = // - new IdentityHashMap(); + new IdentityHashMap<>(); // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/InterruptionChecker.java b/src/main/java/nonapi/io/github/classgraph/concurrency/InterruptionChecker.java index 9d74c12e2..83b500caa 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/InterruptionChecker.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/InterruptionChecker.java @@ -42,7 +42,7 @@ public class InterruptionChecker { /** The first {@link ExecutionException} that was thrown. */ private final AtomicReference thrownExecutionException = // - new AtomicReference(); + new AtomicReference<>(); /** Interrupt all threads that share this InterruptionChecker. */ public void interrupt() { diff --git a/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java b/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java index 148f0a7eb..eb1a73cc1 100644 --- a/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java +++ b/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java @@ -98,7 +98,7 @@ public T acquire() throws E { * If anything goes wrong when trying to allocate a new object instance. */ public RecycleOnClose acquireRecycleOnClose() throws E { - return new RecycleOnClose(this, acquire()); + return new RecycleOnClose<>(this, acquire()); } /** From dddc9a11944680eda4a0817d230d63e5cc34a8b7 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:39:25 +0200 Subject: [PATCH 090/713] Dangling Javadoc comment --- .../java/io/github/classgraph/ClasspathElementModule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 80270b169..3ee78980e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -54,10 +54,10 @@ import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; -/** A module classpath element. */ /** - * @author luke + * A module classpath element. * + * @author luke */ class ClasspathElementModule extends ClasspathElement { From 0ac619d192740723f44bcd2e25ad82539e08db95 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:40:47 +0200 Subject: [PATCH 091/713] Declaration has Javadoc problems --- src/main/java/nonapi/io/github/classgraph/utils/Assert.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java index 8ca51d781..3a35efd5f 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java @@ -7,7 +7,7 @@ public final class Assert { * * @param clazz * the class. - * @throw {@link IllegalArgumentException} if the class is not an annotation. + * @throws {@link IllegalArgumentException} if the class is not an annotation. */ public static void isAnnotation(final Class clazz) { if (!clazz.isAnnotation()) { @@ -20,7 +20,7 @@ public static void isAnnotation(final Class clazz) { * * @param clazz * the class. - * @throw {@link IllegalArgumentException} if the class is not an interface. + * @throws {@link IllegalArgumentException} if the class is not an interface. */ public static void isInterface(final Class clazz) { if (!clazz.isInterface()) { From f6e25e0bd5bd7228aa97e073e812aaaf54549823 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:44:15 +0200 Subject: [PATCH 092/713] Bulk operation can be used instead of iteration --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 4 +--- src/main/java/io/github/classgraph/MethodInfo.java | 8 ++------ src/main/java/io/github/classgraph/ModulePathInfo.java | 6 ++---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 67fa18e2a..73a2b5dd1 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -98,9 +98,7 @@ public class ClassGraphClassLoader extends ClassLoader { final ClassLoader[] envClassLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); if (envClassLoaderOrder != null) { // Try environment classloaders - for (final ClassLoader envClassLoader : envClassLoaderOrder) { - environmentClassLoaderDelegationOrder.add(envClassLoader); - } + environmentClassLoaderDelegationOrder.addAll(Arrays.asList(envClassLoaderOrder)); } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index d42da3da9..84bb5a51f 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -777,9 +777,7 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) } if (hasRepeatableAnnotation) { final AnnotationInfoList aiList = new AnnotationInfoList(pai.length); - for (final AnnotationInfo ai : pai) { - aiList.add(ai); - } + aiList.addAll(Arrays.asList(pai)); aiList.handleRepeatableAnnotations(allRepeatableAnnotationNames, getClassInfo(), RelType.METHOD_PARAMETER_ANNOTATIONS, RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, @@ -1065,9 +1063,7 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { annotationsToExclude = null; } else { annotationsToExclude = new AnnotationInfoList(paramInfo.annotationInfo.length); - for (int j = 0; j < paramInfo.annotationInfo.length; j++) { - annotationsToExclude.add(paramInfo.annotationInfo[j]); - } + annotationsToExclude.addAll(Arrays.asList(paramInfo.annotationInfo)); } paramTypeSignature.toStringInternal(useSimpleNames, annotationsToExclude, buf); } diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 2cf1c6c2f..de698d99b 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -154,10 +154,8 @@ public ModulePathInfo() { argField.add(argParam); } else { // Split arg param into parts - for (final String argPart : JarUtils.smartPathSplit(argParam, sepChar, - /* scanSpec = */ null)) { - argField.add(argPart); - } + argField.addAll(Arrays.asList(JarUtils.smartPathSplit(argParam, sepChar, + /* scanSpec = */ null))); } } } From 9bcdc6cdd3ed82cbbe6ef91098d5358088aeaa16 Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:45:02 +0200 Subject: [PATCH 093/713] Call to 'Arrays.asList()' with too few arguments --- .../WebsphereLibertyClassLoaderHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index 835f5bff8..b4784f778 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -142,7 +142,7 @@ private static Collection getPaths(final Object containerClassLoader) { final String path = (String) ReflectionUtils.getFieldVal(delegate, "path", false); if (path != null && path.length() > 0) { - return Arrays.asList((Object) path); + return Collections.singletonList((Object) path); } final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); @@ -154,7 +154,7 @@ private static Collection getPaths(final Object containerClassLoader) { final Object archiveFile = ReflectionUtils.getFieldVal(base, "archiveFile", false); if (archiveFile != null) { final File file = (File) archiveFile; - return Arrays.asList((Object) file.getAbsolutePath()); + return Collections.singletonList((Object) file.getAbsolutePath()); } return Collections. emptyList(); } From 7d546393830fd984ee280deaf9198f9b5955273d Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:46:51 +0200 Subject: [PATCH 094/713] String concatenation as argument to 'StringBuilder.append()' call --- src/main/java/io/github/classgraph/AnnotationClassRef.java | 3 ++- src/main/java/io/github/classgraph/ClassTypeSignature.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index d3b3392e4..7ccf67a5f 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -214,6 +214,7 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { // } // } - buf.append(/* prefix + */ getTypeSignature().toString(useSimpleNames) + ".class"); + /* prefix + */ + buf.append(getTypeSignature().toString(useSimpleNames)).append(".class"); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 2e9b77b2b..d8c0303ca 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -275,7 +275,7 @@ void toStringInternal(final String className, final boolean useSimpleNames, fina if (buf.length() > 0) { buf.append(' '); } - buf.append("@throws(" + throwsSignature + ")"); + buf.append("@throws(").append(throwsSignature).append(")"); } } if (modifiers != 0) { From 7c185f05f8e50f51e882351441d43822a30b755a Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:48:46 +0200 Subject: [PATCH 095/713] Redundant type arguments --- .../io/github/classgraph/ClassGraphClassLoader.java | 2 +- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- .../WebsphereLibertyClassLoaderHandler.java | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 73a2b5dd1..3235fc612 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -389,7 +389,7 @@ public Enumeration getResources(final String path) throws IOException { // This will throw an exception if ScanResult has already been closed (#399). final ResourceList resourceList = scanResult.getResourcesWithPath(path); if (resourceList == null || resourceList.isEmpty()) { - return Collections. emptyEnumeration(); + return Collections.emptyEnumeration(); } else { return new Enumeration() { /** The idx. */ diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 8d8d56fa1..876ef9243 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -780,7 +780,7 @@ private enum ClassType { private static Set filterClassInfo(final Collection classes, final ScanSpec scanSpec, final boolean strictAccept, final ClassType... classTypes) { if (classes == null) { - return Collections. emptySet(); + return Collections.emptySet(); } boolean includeAllTypes = classTypes.length == 0; boolean includeStandardClasses = false; diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index b4784f778..f74074843 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -110,7 +110,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ private static Collection getPaths(final Object containerClassLoader) { if (containerClassLoader == null) { - return Collections. emptyList(); + return Collections.emptyList(); } // Expecting this to be an instance of @@ -124,7 +124,7 @@ private static Collection getPaths(final Object containerClassLoader) { // "getContainerURLs" didn't work, try getting the container object... final Object container = ReflectionUtils.getFieldVal(containerClassLoader, "container", false); if (container == null) { - return Collections. emptyList(); + return Collections.emptyList(); } // Should be an instance of "com.ibm.wsspi.adaptable.module.Container". @@ -137,7 +137,7 @@ private static Collection getPaths(final Object containerClassLoader) { // "getURLs" did not work, reverting to previous logic of introspection of the "delegate". final Object delegate = ReflectionUtils.getFieldVal(container, "delegate", false); if (delegate == null) { - return Collections. emptyList(); + return Collections.emptyList(); } final String path = (String) ReflectionUtils.getFieldVal(delegate, "path", false); @@ -148,7 +148,7 @@ private static Collection getPaths(final Object containerClassLoader) { final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); if (base == null) { // giving up. - return Collections. emptyList(); + return Collections.emptyList(); } final Object archiveFile = ReflectionUtils.getFieldVal(base, "archiveFile", false); @@ -156,7 +156,7 @@ private static Collection getPaths(final Object containerClassLoader) { final File file = (File) archiveFile; return Collections.singletonList((Object) file.getAbsolutePath()); } - return Collections. emptyList(); + return Collections.emptyList(); } /** @@ -196,7 +196,7 @@ private static Collection callGetUrls(final Object container, final Stri /* ignore */ } } - return Collections. emptyList(); + return Collections.emptyList(); } /** From bd6a608ea4e869b61d77e23b5a2471f8ac97e6ba Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:49:16 +0200 Subject: [PATCH 096/713] Redundant type cast --- src/main/java/io/github/classgraph/AnnotationClassRef.java | 6 +++--- src/main/java/io/github/classgraph/ArrayTypeSignature.java | 2 +- src/main/java/io/github/classgraph/Scanner.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 7ccf67a5f..3ac4343ac 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -107,9 +107,9 @@ public Class loadClass(final boolean ignoreExceptions) { if (typeSignature instanceof BaseTypeSignature) { return ((BaseTypeSignature) typeSignature).getType(); } else if (typeSignature instanceof ClassRefTypeSignature) { - return ((ClassRefTypeSignature) typeSignature).loadClass(ignoreExceptions); + return typeSignature.loadClass(ignoreExceptions); } else if (typeSignature instanceof ArrayTypeSignature) { - return ((ArrayTypeSignature) typeSignature).loadClass(ignoreExceptions); + return typeSignature.loadClass(ignoreExceptions); } else { throw new IllegalArgumentException("Got unexpected type " + typeSignature.getClass().getName() + " for ref type signature: " + typeDescriptorStr); @@ -142,7 +142,7 @@ protected String getClassName() { } else if (typeSignature instanceof ClassRefTypeSignature) { className = ((ClassRefTypeSignature) typeSignature).getFullyQualifiedClassName(); } else if (typeSignature instanceof ArrayTypeSignature) { - className = ((ArrayTypeSignature) typeSignature).getClassName(); + className = typeSignature.getClassName(); } else { throw new IllegalArgumentException("Got unexpected type " + typeSignature.getClass().getName() + " for ref type signature: " + typeDescriptorStr); diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index 1774904f1..6565c9e14 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -244,7 +244,7 @@ public Class loadElementClass(final boolean ignoreExceptions) { elementClassRef = elementTypeSignature.loadClass(ignoreExceptions); } else { // Fallback, if scanResult is not set - final String elementTypeName = ((ClassRefTypeSignature) elementTypeSignature).getClassName(); + final String elementTypeName = elementTypeSignature.getClassName(); try { elementClassRef = Class.forName(elementTypeName); } catch (final Throwable t) { diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 1857790fe..91f608df6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -835,7 +835,7 @@ private void preprocessClasspathElementsByType(final List fina for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { if (classpathElt instanceof ClasspathElementFileDir) { // Separate out ClasspathElementDir elements from other types - classpathEltDirs.add(new SimpleEntry<>(((ClasspathElementFileDir) classpathElt).getFile().getPath(), + classpathEltDirs.add(new SimpleEntry<>(classpathElt.getFile().getPath(), classpathElt)); } else if (classpathElt instanceof ClasspathElementZip) { From 2f872ffcc1b77120357e2e62fef012b4739830ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 17:52:37 -0600 Subject: [PATCH 097/713] Add missing method (#549) --- src/main/java/io/github/classgraph/ScanResult.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 613af2b41..682f413b4 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -936,6 +936,17 @@ public ClassInfoList getSuperclasses(final String subclassName) { return subclass == null ? ClassInfoList.EMPTY_LIST : subclass.getSuperclasses(); } + /** + * Get superclasses of the subclass. + * + * @param subclass + * The subclass. + * @return A list of superclasses of the named subclass, or the empty list if none. + */ + public ClassInfoList getSuperclasses(final Class subclass) { + return getSuperclasses(subclass.getName()); + } + /** * Get classes that have a method with an annotation of the named type. * From fb53b498124bdc42ab3c85d4e22931230acde24c Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Tue, 17 Aug 2021 01:54:56 +0200 Subject: [PATCH 098/713] fix imports --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 1 + src/main/java/io/github/classgraph/MethodInfo.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 3235fc612..8f6440dd5 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -34,6 +34,7 @@ import java.net.URLClassLoader; import java.nio.ByteBuffer; import java.security.ProtectionDomain; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 84bb5a51f..7f2fff1ff 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -34,6 +34,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; From 3d503662a238000fe50ad6aad6289be7c137657b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 17:54:56 -0600 Subject: [PATCH 099/713] Add missing method (#549) --- .../java/io/github/classgraph/ScanResult.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 682f413b4..0f1440664 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1063,8 +1063,8 @@ public ClassInfoList getAllInterfaces() { } /** - * Get all interfaces implemented by the named class or by one of its superclasses, if this is a standard class, - * or the superinterfaces extended by this interface, if this is an interface. + * Get all interfaces implemented by the named class or by one of its superclasses, if the named class is a + * standard class, or the superinterfaces extended by this interface, if it is an interface. * * @param className * The class name. @@ -1082,6 +1082,19 @@ public ClassInfoList getInterfaces(final String className) { return classInfo == null ? ClassInfoList.EMPTY_LIST : classInfo.getInterfaces(); } + /** + * Get all interfaces implemented by the class or by one of its superclasses, if the given class is a standard + * class, or the superinterfaces extended by this interface, if it is an interface. + * + * @param classRef + * The class. + * @return A list of interfaces implemented by the given class (or superinterfaces extended by the given + * interface), or the empty list if none. + */ + public ClassInfoList getInterfaces(final Class classRef) { + return getInterfaces(classRef.getName()); + } + /** * Get all classes that implement (or have superclasses that implement) the interface (or one of its * subinterfaces). From a8de934102c6016112cf11c7cecd6e087796bc71 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 18:14:44 -0600 Subject: [PATCH 100/713] Improve clarity of code change suggested by static analyzer (#550) --- src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java index 4f880cc3f..d3f473970 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -76,7 +76,7 @@ public final class JSONUtils { final char hexDigit1 = nibble1 <= 9 ? (char) ('0' + nibble1) : (char) ('A' + nibble1 - 10); final int nibble0 = c & 0xf; final char hexDigit0 = nibble0 <= 9 ? (char) ('0' + nibble0) : (char) ('A' + nibble0 - 10); - JSON_CHAR_REPLACEMENTS[c] = "\\u00" + hexDigit1 + hexDigit0; + JSON_CHAR_REPLACEMENTS[c] = "\\u00" + hexDigit1 + "" + hexDigit0; } JSON_CHAR_REPLACEMENTS['"'] = "\\\""; JSON_CHAR_REPLACEMENTS['\\'] = "\\\\"; From 4fc27be9ae6c378d6c9c7b16da2d75293dcb3167 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 16 Aug 2021 18:16:53 -0600 Subject: [PATCH 101/713] Source > Cleanup --- src/main/java/io/github/classgraph/ClassTypeSignature.java | 4 ++-- src/main/java/io/github/classgraph/ModulePathInfo.java | 4 ++-- src/main/java/io/github/classgraph/ModuleRef.java | 3 +-- src/main/java/io/github/classgraph/Scanner.java | 3 +-- .../WebsphereLibertyClassLoaderHandler.java | 1 - .../classgraph/fileslice/reader/RandomAccessReader.java | 2 +- src/main/java/nonapi/io/github/classgraph/utils/Assert.java | 6 ++++-- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index d8c0303ca..4fc34045d 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -415,7 +415,7 @@ static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo cla if (parser.hasMore()) { throw new ParseException(parser, "Extra characters at end of type descriptor"); } - return new ClassTypeSignature(classInfo, typeParameters, - superclassSignature, superinterfaceSignatures, throwsSignatures); + return new ClassTypeSignature(classInfo, typeParameters, superclassSignature, superinterfaceSignatures, + throwsSignatures); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index de698d99b..c79871632 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -154,8 +154,8 @@ public ModulePathInfo() { argField.add(argParam); } else { // Split arg param into parts - argField.addAll(Arrays.asList(JarUtils.smartPathSplit(argParam, sepChar, - /* scanSpec = */ null))); + argField.addAll(Arrays + .asList(JarUtils.smartPathSplit(argParam, sepChar, /* scanSpec = */ null))); } } } diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 23ebe44c1..0484d3716 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -93,8 +93,7 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { // Should not happen throw new IllegalArgumentException("moduleReference.descriptor() should not return null"); } - this.name = (String) ReflectionUtils.invokeMethod(this.descriptor, "name", - /* throwException = */ true); + this.name = (String) ReflectionUtils.invokeMethod(this.descriptor, "name", /* throwException = */ true); @SuppressWarnings("unchecked") final Set modulePackages = (Set) ReflectionUtils.invokeMethod(this.descriptor, "packages", /* throwException = */ true); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 91f608df6..c8497c73c 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -835,8 +835,7 @@ private void preprocessClasspathElementsByType(final List fina for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { if (classpathElt instanceof ClasspathElementFileDir) { // Separate out ClasspathElementDir elements from other types - classpathEltDirs.add(new SimpleEntry<>(classpathElt.getFile().getPath(), - classpathElt)); + classpathEltDirs.add(new SimpleEntry<>(classpathElt.getFile().getPath(), classpathElt)); } else if (classpathElt instanceof ClasspathElementZip) { // Separate out ClasspathElementZip elements from other types diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index f74074843..2d804bd52 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -32,7 +32,6 @@ import java.io.File; import java.net.URL; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java index 8842afa25..65f6b3816 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java @@ -161,7 +161,7 @@ public interface RandomAccessReader { * If an I/O exception occurs. */ String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, - final boolean stripLSemicolon) throws IOException; + final boolean stripLSemicolon) throws IOException; /** * Reads the "modified UTF8" format defined in the Java classfile spec. diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java index 3a35efd5f..936786b9d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/Assert.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/Assert.java @@ -7,7 +7,8 @@ public final class Assert { * * @param clazz * the class. - * @throws {@link IllegalArgumentException} if the class is not an annotation. + * @throws IllegalArgumentException + * if the class is not an annotation. */ public static void isAnnotation(final Class clazz) { if (!clazz.isAnnotation()) { @@ -20,7 +21,8 @@ public static void isAnnotation(final Class clazz) { * * @param clazz * the class. - * @throws {@link IllegalArgumentException} if the class is not an interface. + * @throws IllegalArgumentException + * if the class is not an interface. */ public static void isInterface(final Class clazz) { if (!clazz.isInterface()) { From 44acd68066abaa911ddf277f2746fda5e8d4c290 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Aug 2021 02:01:14 +0000 Subject: [PATCH 102/713] Bump slf4j-api from 2.0.0-alpha4 to 2.0.0-alpha5 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha4 to 2.0.0-alpha5. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha4...v_2.0.0-alpha5) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0457dc958..f1eecd4ac 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.slf4j slf4j-api - 2.0.0-alpha4 + 2.0.0-alpha5 test From 3f4d2c40a57357e8ea7789f3eddd679c7c39b71c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Aug 2021 02:01:18 +0000 Subject: [PATCH 103/713] Bump slf4j-jdk14 from 2.0.0-alpha4 to 2.0.0-alpha5 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha4 to 2.0.0-alpha5. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha4...v_2.0.0-alpha5) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0457dc958..18b1da330 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha4 + 2.0.0-alpha5 test From 97459465b2d43e8ffb1011b208d0fa0324016b07 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Sep 2021 21:33:34 -0600 Subject: [PATCH 104/713] Catch Exception when processing work units (#553) --- src/main/java/io/github/classgraph/Scanner.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index c8497c73c..ec60ffd22 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -749,6 +749,10 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, if (subLog != null) { subLog.log(workUnit.classfileResource.getPath(), "Could not read classfile: " + e); } + } catch (final Exception e) { + if (subLog != null) { + subLog.log(workUnit.classfileResource.getPath(), "Could not read classfile", e); + } } finally { if (subLog != null) { subLog.addElapsedTime(); From c2792fc618b934cfcff5c5fddb5352dee8daf368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Sep 2021 02:01:35 +0000 Subject: [PATCH 105/713] Bump maven-javadoc-plugin from 3.3.0 to 3.3.1 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.0...maven-javadoc-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0457dc958..9081f1774 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins From 6ca4484ed9a92ab1f5578dc7f0fa676069effb23 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Sep 2021 02:55:42 -0600 Subject: [PATCH 106/713] Ignore JrtFileSystem (#553) --- src/main/java/io/github/classgraph/Scanner.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index ec60ffd22..bdc74ab75 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -482,6 +482,13 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa return new ClasspathElementZip(classpathEntryPath, classpathEntry.classLoader, nestedJarHandler, scanSpec); } else if (FileUtils.canReadAndIsDir(packageRootPath)) { + if ("JrtFileSystem" + .equals(packageRootPath.getFileSystem().getClass().getSimpleName())) { + // Ignore JrtFileSystem (#553) -- paths are of form: + // /modules/java.base/module-info.class + throw new IOException("Ignoring JrtFS filesystem path " + packageRootPath + + " (modules are scanned using the JPMS API)"); + } // classpathEntryObj is a Path which points to a dir -- need to scan it recursively return new ClasspathElementPathDir(classpathEntryPath, dirOrPathPackageRoot, classpathEntry.classLoader, nestedJarHandler, scanSpec); From d89f2331d829242683e940f06e566abf44681599 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Sep 2021 02:57:13 -0600 Subject: [PATCH 107/713] [maven-release-plugin] prepare release classgraph-4.8.116 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 42cc59e03..e766c94ed 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.116-SNAPSHOT + 4.8.116 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.116 From f00ff6b2f008adf17cc4ee61a0a86c07a4accae1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Sep 2021 02:57:16 -0600 Subject: [PATCH 108/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e766c94ed..2f87646a3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.116 + 4.8.117-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.116 + classgraph-4.8.105 From e80af3f5860d70687d18da7386558ab9376bc84b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 Sep 2021 19:41:15 -0600 Subject: [PATCH 109/713] Try to fetch `ucp` field (will only work with Narcissus) --- .../classloaderhandler/JPMSClassLoaderHandler.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java index 3c4802881..0d1dadf8d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -28,10 +28,13 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import java.net.URL; + import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * A placeloader ClassLoaderHandler that matches Java 9+ classloaders, but does not attempt to extract URLs from @@ -68,9 +71,6 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, final LogNode log) { - // Add JPMS classloaders into classloader order, so that they can be used for classloading - // (e.g. by ClassInfo#loadClass()). However, findClasspathOrder() below cannot actually find - // classpath element locations from JPMS classloaders, so the method body is blank. classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); classLoaderOrder.add(classLoader, log); } @@ -91,5 +91,13 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final ScanSpec scanSpec, final LogNode log) { // The JDK9 classloaders have a field, `URLClassPath ucp`, containing URLs for unnamed modules, // but it is not visible. Modules therefore have to be scanned using the JPMS API. + // However, it is possible for a Java agent to extend UCP by adding directly to the `ucp` field + // (#537), and there is no way to read this field. Therefore, we need to use Narcissus to break + // Java's encapsulation to read this, for this small corner case. + final Object ucpVal = ReflectionUtils.getFieldVal(classLoader, "ucp", false); + if (ucpVal != null) { + final URL[] urls = (URL[]) ReflectionUtils.invokeMethod(ucpVal, "getURLs", false); + classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); + } } } From 04267a4bc7d8536b67cd0da40d5c377c88c2dd35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 02:00:57 +0000 Subject: [PATCH 110/713] Bump assertj-core from 3.20.2 to 3.21.0 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.20.2 to 3.21.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.20.2...assertj-core-3.21.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f87646a3..04fab24c5 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.20.2 + 3.21.0 test From d2f44e544bae5f951c35fb4a1f3eb8121b364ec6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Sep 2021 02:01:16 +0000 Subject: [PATCH 111/713] Bump junit-jupiter from 5.7.2 to 5.8.1 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.7.2 to 5.8.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.2...r5.8.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f87646a3..83af6fff0 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.junit.jupiter junit-jupiter - 5.7.2 + 5.8.1 test From 3fc55ae1f99c28af4edb09e90d7f1ef2c160389e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Sep 2021 17:13:29 -0600 Subject: [PATCH 112/713] Enable testing on JDK 17 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc057d788..284c7a503 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15', '16' ] + java: [ '8', '11', '12', '13', '14', '15', '16', '17' ] steps: - uses: actions/checkout@v2.3.4 - name: Set up JDK From 2612e460fdb6d07effa71ac7d9ce5618bb5ff3bd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 16:20:15 -0600 Subject: [PATCH 113/713] Remove unnecessary annotation --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java index 5da03c36c..ed16916ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -43,7 +43,6 @@ public class ClassLoaderHandlerRegistry { /** * Default ClassLoaderHandlers. If a ClassLoaderHandler is added to ClassGraph, it should be added to this list. */ - @SuppressWarnings("null") public static final List CLASS_LOADER_HANDLERS = // Collections.unmodifiableList(Arrays.asList( // ClassLoaderHandlers for other ClassLoaders that are handled by ClassGraph From 1809312e9bacfa963e8c96eeee36b794efa00e20 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 16:27:44 -0600 Subject: [PATCH 114/713] Use toolfactory/jvm-driver for reflection --- pom.xml | 26 +- .../classgraph/utils/ReflectionUtils.java | 235 ++++++++---------- 2 files changed, 119 insertions(+), 142 deletions(-) diff --git a/pom.xml b/pom.xml index 36812722c..cf0fc7b83 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -63,9 +66,16 @@ - + + + io.github.toolfactory + jvm-driver + 4.0.0 + + + org.junit.jupiter junit-jupiter @@ -268,7 +278,8 @@ - + org.codehaus.mojo.signature java17 @@ -347,8 +358,13 @@ - - + + diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 200dd8389..9bfb6e7c4 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -29,7 +29,6 @@ package nonapi.io.github.classgraph.utils; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; @@ -37,12 +36,16 @@ import java.util.List; import java.util.Set; +import io.github.toolfactory.jvm.DefaultDriver; +import io.github.toolfactory.jvm.Driver; + /** Reflection utility methods that can be used by ClassLoaderHandlers. */ public final class ReflectionUtils { - - // In JDK 9+, could use MethodHandles.privateLookupIn - // And then use getter lookup to get fields (which works even if there is no getter function defined): - // https://stackoverflow.com/q/19135218/3950982 + /** + * Use jvm-driver to bypass Java visibility restrictions, security manager limitations, and strong + * encapsulation. + */ + private static Driver reflectionDriver = new DefaultDriver(); /** * Constructor. @@ -71,34 +74,18 @@ private ReflectionUtils() { */ private static Object getFieldVal(final Class cls, final Object obj, final String fieldName, final boolean throwException) throws IllegalArgumentException { - Field field = null; - for (Class currClass = cls; currClass != null; currClass = currClass.getSuperclass()) { - try { - field = currClass.getDeclaredField(fieldName); - // Field found - break; - } catch (final ReflectiveOperationException | SecurityException e) { - // Try parent + try { + for (Class c = cls; c != null; c = c.getSuperclass()) { + for (Field field : reflectionDriver.getDeclaredFields(c)) { + if (field.getName().equals(fieldName)) { + return reflectionDriver.getFieldValue(obj, field); + } + } } - } - if (field == null) { + } catch (Exception e) { if (throwException) { - throw new IllegalArgumentException((obj == null ? "Static field " : "Field ") + "\"" + fieldName - + "\" not found or not accessible"); - } - } else { - try { - field.setAccessible(true); - } catch (final RuntimeException e) { // JDK 9+: InaccessibleObjectException | SecurityException - // Ignore - } - try { - return field.get(obj); - } catch (final IllegalAccessException e) { - if (throwException) { - throw new IllegalArgumentException( - "Can't read " + (obj == null ? "static " : "") + " field \"" + fieldName + "\": " + e); - } + throw new IllegalArgumentException( + "Can't read " + (obj == null ? "static " : "") + " field \"" + fieldName + "\": " + e); } } return null; @@ -161,116 +148,44 @@ public static Object getStaticFieldVal(final Class cls, final String fieldNam } /** - * Iterate through implemented interfaces, top-down, then superclass to subclasses, top-down (since higher-up - * superclasses and superinterfaces have the highest chance of being visible). + * Get order in which to attempt invoking methods. * * @param cls * the class - * @return the reverse of the order in which method calls would be attempted by the JRE. + * @return the order in which method calls would be attempted by the JRE. */ - private static List> getReverseMethodAttemptOrder(final Class cls) { - final List> reverseAttemptOrder = new ArrayList<>(); - - // Iterate from class to its superclasses - for (Class c = cls; c != null && c != Object.class; c = c.getSuperclass()) { - reverseAttemptOrder.add(c); - } - - // Find interfaces and superinterfaces implemented by this class or its superclasses - final Set> addedIfaces = new HashSet<>(); - final LinkedList> ifaceQueue = new LinkedList<>(); + private static List enumerateMethods(final Class cls) { + // Iterate from class to its superclasses, and find initial interfaces to start traversing from + final List methodOrder = new ArrayList<>(); + final Set> visited = new HashSet<>(); + final LinkedList> interfaceQueue = new LinkedList<>(); for (Class c = cls; c != null; c = c.getSuperclass()) { - if (c.isInterface() && addedIfaces.add(c)) { - ifaceQueue.add(c); + for (Method m : reflectionDriver.getDeclaredMethods(c)) { + methodOrder.add(m); } - for (final Class iface : c.getInterfaces()) { - if (addedIfaces.add(iface)) { - ifaceQueue.add(iface); - } + // Find interfaces and superinterfaces implemented by this class or its superclasses + if (c.isInterface() && visited.add(c)) { + interfaceQueue.add(c); } - } - while (!ifaceQueue.isEmpty()) { - final Class iface = ifaceQueue.remove(); - reverseAttemptOrder.add(iface); - final Class[] superIfaces = iface.getInterfaces(); - if (superIfaces.length > 0) { - for (final Class superIface : superIfaces) { - if (addedIfaces.add(superIface)) { - ifaceQueue.add(superIface); - } + for (final Class iface : c.getInterfaces()) { + if (visited.add(iface)) { + interfaceQueue.add(iface); } } } - return reverseAttemptOrder; - } - - /** - * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to - * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, - * otherwise this will return null. If passed a null object, returns null unless throwException is true, then - * throws IllegalArgumentException. - * - * @param cls - * The class. - * @param obj - * The object, or null to invoke a static method. - * @param methodName - * The method name. - * @param oneArg - * If true, look for a method with one argument of type argType. If false, look for method with no - * arguments. - * @param argType - * The type of the first argument to the method. - * @param param - * The value of the first parameter to invoke the method with. - * @param throwException - * If true, throw an exception if the field value could not be read. - * @return The result of the method invocation. - * @throws IllegalArgumentException - * If the field value could not be read. - */ - private static Object invokeMethod(final Class cls, final Object obj, final String methodName, - final boolean oneArg, final Class argType, final Object param, final boolean throwException) - throws IllegalArgumentException { - Method method = null; - final List> reverseAttemptOrder = getReverseMethodAttemptOrder(cls); - for (int i = reverseAttemptOrder.size() - 1; i >= 0; i--) { - final Class classOrInterface = reverseAttemptOrder.get(i); - try { - method = oneArg ? classOrInterface.getDeclaredMethod(methodName, argType) - : classOrInterface.getDeclaredMethod(methodName); - // Method found - break; - } catch (final ReflectiveOperationException | SecurityException e) { - // Try next interface or superclass + // Traverse through interfaces looking for default methods + while (!interfaceQueue.isEmpty()) { + final Class iface = interfaceQueue.remove(); + for (Method m : reflectionDriver.getDeclaredMethods(iface)) { + methodOrder.add(m); } - } - if (method == null) { - if (throwException) { - throw new IllegalArgumentException((obj == null ? "Static method " : "Method ") + "\"" + methodName - + "\" not found or not accesible"); - } - } else { - try { - method.setAccessible(true); - } catch (final RuntimeException e) { // JDK 9+: InaccessibleObjectException | SecurityException - // Ignore - } - try { - return oneArg ? method.invoke(obj, param) : method.invoke(obj); - } catch (final IllegalAccessException | SecurityException e) { - if (throwException) { - throw new IllegalArgumentException( - "Can't call " + (obj == null ? "static " : "") + "method \"" + methodName + "\": " + e); - } - } catch (final InvocationTargetException e) { - if (throwException) { - throw new IllegalArgumentException("Exception while invoking " + (obj == null ? "static " : "") - + "method \"" + methodName + "\"", e); + for (final Class superIface : iface.getInterfaces()) { + if (visited.add(superIface)) { + interfaceQueue.add(superIface); } } } - return null; + return methodOrder; } /** @@ -293,12 +208,23 @@ public static Object invokeMethod(final Object obj, final String methodName, fin throws IllegalArgumentException { if (obj == null || methodName == null) { if (throwException) { - throw new NullPointerException(); + throw new IllegalArgumentException("Unexpected null argument"); } else { return null; } } - return invokeMethod(obj.getClass(), obj, methodName, false, null, null, throwException); + try { + for (Method m : enumerateMethods(obj.getClass())) { + if (m.getName().equals(methodName) && m.getParameterTypes().length == 0) { + return reflectionDriver.invoke(m, obj, new Object[0]); + } + } + } catch (Exception e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + } + return null; } /** @@ -323,14 +249,26 @@ public static Object invokeMethod(final Object obj, final String methodName, fin */ public static Object invokeMethod(final Object obj, final String methodName, final Class argType, final Object param, final boolean throwException) throws IllegalArgumentException { - if (obj == null || methodName == null) { + if (obj == null || methodName == null || argType == null) { if (throwException) { - throw new NullPointerException(); + throw new IllegalArgumentException("Unexpected null argument"); } else { return null; } } - return invokeMethod(obj.getClass(), obj, methodName, true, argType, param, throwException); + try { + for (Method m : enumerateMethods(obj.getClass())) { + if (m.getName().equals(methodName) && m.getParameterTypes().length == 1 + && m.getParameterTypes()[0] == argType) { + return reflectionDriver.invoke(m, obj, new Object[] { param }); + } + } + } catch (Exception e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + } + return null; } /** @@ -353,12 +291,23 @@ public static Object invokeStaticMethod(final Class cls, final String methodN final boolean throwException) throws IllegalArgumentException { if (cls == null || methodName == null) { if (throwException) { - throw new NullPointerException(); + throw new IllegalArgumentException("Unexpected null argument"); } else { return null; } } - return invokeMethod(cls, null, methodName, false, null, null, throwException); + try { + for (Method m : enumerateMethods(cls)) { + if (m.getName().equals(methodName) && m.getParameterTypes().length == 0) { + return reflectionDriver.invoke(m, null, new Object[0]); + } + } + } catch (Exception e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + } + return null; } /** @@ -383,14 +332,26 @@ public static Object invokeStaticMethod(final Class cls, final String methodN */ public static Object invokeStaticMethod(final Class cls, final String methodName, final Class argType, final Object param, final boolean throwException) throws IllegalArgumentException { - if (cls == null || methodName == null) { + if (cls == null || methodName == null || argType == null) { if (throwException) { - throw new NullPointerException(); + throw new IllegalArgumentException("Unexpected null argument"); } else { return null; } } - return invokeMethod(cls, null, methodName, true, argType, param, throwException); + try { + for (Method m : enumerateMethods(cls)) { + if (m.getName().equals(methodName) && m.getParameterTypes().length == 1 + && m.getParameterTypes()[0] == argType) { + return reflectionDriver.invoke(m, null, new Object[] { param }); + } + } + } catch (Exception e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + } + return null; } /** From 82d965f8e90cb915ce41fdc1ecfdfbba13389b53 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 16:29:04 -0600 Subject: [PATCH 115/713] [maven-release-plugin] prepare release classgraph-4.8.117 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index cf0fc7b83..76b45ffcd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.117-SNAPSHOT + 4.8.117 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.117 @@ -278,8 +275,7 @@ - + org.codehaus.mojo.signature java17 @@ -358,13 +354,8 @@ - - + + From f28073791aa8d99f40106b0d1bd5c8367205aec1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 16:29:06 -0600 Subject: [PATCH 116/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 76b45ffcd..7e681214c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.117 + 4.8.118-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.117 + classgraph-4.8.105 From e44283fd3dd3a0eca4bfe6a135c4c28694f1cb2e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 17:06:48 -0600 Subject: [PATCH 117/713] Add getAnnotationInfoDirectOnly() (#559) --- src/main/java/io/github/classgraph/ClassInfo.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 876ef9243..5099f5dd1 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1945,6 +1945,20 @@ public AnnotationInfoList getAnnotationInfo() { return AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); } + /** + * Get a list of the direct annotations on this class (i.e. not including meta-annotations or inherited + * annotations), or the empty list if none. + * + * @return A list of {@link AnnotationInfo} objects for the direct annotations on this class, or the empty list + * if none. + */ + public AnnotationInfoList getAnnotationInfoDirectOnly() { + if (!scanResult.scanSpec.enableAnnotationInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); + } + return annotationInfo; + } + /** * Get a the non-{@link Repeatable} annotation on this class, or null if the class does not have the annotation. * (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) From 728034fbc8d035edda58de9ad77efd7e92711bfc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 28 Sep 2021 17:09:05 -0600 Subject: [PATCH 118/713] Amend previous commit --- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 5099f5dd1..57e3f5bcf 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1956,7 +1956,7 @@ public AnnotationInfoList getAnnotationInfoDirectOnly() { if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - return annotationInfo; + return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST : annotationInfo; } /** From d0b3b4bf85c0bf7edb94238124f35075baeb9dff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 29 Sep 2021 00:44:15 -0600 Subject: [PATCH 119/713] Refactoring and optimization --- .../classgraph/utils/ReflectionUtils.java | 102 +++++++++++------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 9bfb6e7c4..15e103bca 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -30,11 +30,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; -import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import io.github.toolfactory.jvm.DefaultDriver; import io.github.toolfactory.jvm.Driver; @@ -147,21 +147,28 @@ public static Object getStaticFieldVal(final Class cls, final String fieldNam return getFieldVal(cls, null, fieldName, throwException); } + /** Iterator applied to each method of a class and its superclasses/interfaces. */ + private static interface MethodIterator { + /** @return true to stop iterating, or false to continue iterating */ + boolean foundMethod(Method m); + } + /** - * Get order in which to attempt invoking methods. + * Iterate through all methods in the given class, ignoring visibility and bypassing security checks. Also + * iterates up through superclasses, to collect all methods of the class and its superclasses. * * @param cls * the class - * @return the order in which method calls would be attempted by the JRE. */ - private static List enumerateMethods(final Class cls) { + private static void forAllMethods(final Class cls, final MethodIterator methodIter) { // Iterate from class to its superclasses, and find initial interfaces to start traversing from - final List methodOrder = new ArrayList<>(); final Set> visited = new HashSet<>(); final LinkedList> interfaceQueue = new LinkedList<>(); for (Class c = cls; c != null; c = c.getSuperclass()) { - for (Method m : reflectionDriver.getDeclaredMethods(c)) { - methodOrder.add(m); + for (final Method m : reflectionDriver.getDeclaredMethods(c)) { + if (methodIter.foundMethod(m)) { + return; + } } // Find interfaces and superinterfaces implemented by this class or its superclasses if (c.isInterface() && visited.add(c)) { @@ -176,8 +183,10 @@ private static List enumerateMethods(final Class cls) { // Traverse through interfaces looking for default methods while (!interfaceQueue.isEmpty()) { final Class iface = interfaceQueue.remove(); - for (Method m : reflectionDriver.getDeclaredMethods(iface)) { - methodOrder.add(m); + for (final Method m : reflectionDriver.getDeclaredMethods(iface)) { + if (methodIter.foundMethod(m)) { + return; + } } for (final Class superIface : iface.getInterfaces()) { if (visited.add(superIface)) { @@ -185,7 +194,41 @@ private static List enumerateMethods(final Class cls) { } } } - return methodOrder; + } + + /** + * Find a method by name and parameter types in the given class, ignoring visibility and bypassing security + * checks. + * + * @param cls + * the class + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name + */ + private static Method findMethod(final Class cls, final String methodName, final Class... paramTypes) + throws NoSuchMethodException { + final AtomicReference method = new AtomicReference<>(); + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + method.set(m); + return true; + } + return false; + } + }); + final Method m = method.get(); + if (m != null) { + return m; + } else { + throw new NoSuchMethodException(methodName); + } } /** @@ -202,7 +245,7 @@ private static List enumerateMethods(final Class cls) { * If true, throw an exception if the field value could not be read. * @return The result of the method invocation. * @throws IllegalArgumentException - * If the field value could not be read. + * If the method could not be invoked. */ public static Object invokeMethod(final Object obj, final String methodName, final boolean throwException) throws IllegalArgumentException { @@ -214,17 +257,13 @@ public static Object invokeMethod(final Object obj, final String methodName, fin } } try { - for (Method m : enumerateMethods(obj.getClass())) { - if (m.getName().equals(methodName) && m.getParameterTypes().length == 0) { - return reflectionDriver.invoke(m, obj, new Object[0]); - } - } + return reflectionDriver.invoke(findMethod(obj.getClass(), methodName), obj, new Object[0]); } catch (Exception e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } + return null; } - return null; } /** @@ -257,18 +296,14 @@ public static Object invokeMethod(final Object obj, final String methodName, fin } } try { - for (Method m : enumerateMethods(obj.getClass())) { - if (m.getName().equals(methodName) && m.getParameterTypes().length == 1 - && m.getParameterTypes()[0] == argType) { - return reflectionDriver.invoke(m, obj, new Object[] { param }); - } - } + return reflectionDriver.invoke(findMethod(obj.getClass(), methodName, argType), obj, + new Object[] { param }); } catch (Exception e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } + return null; } - return null; } /** @@ -297,17 +332,13 @@ public static Object invokeStaticMethod(final Class cls, final String methodN } } try { - for (Method m : enumerateMethods(cls)) { - if (m.getName().equals(methodName) && m.getParameterTypes().length == 0) { - return reflectionDriver.invoke(m, null, new Object[0]); - } - } + return reflectionDriver.invoke(findMethod(cls, methodName), null, new Object[0]); } catch (Exception e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } + return null; } - return null; } /** @@ -340,18 +371,13 @@ public static Object invokeStaticMethod(final Class cls, final String methodN } } try { - for (Method m : enumerateMethods(cls)) { - if (m.getName().equals(methodName) && m.getParameterTypes().length == 1 - && m.getParameterTypes()[0] == argType) { - return reflectionDriver.invoke(m, null, new Object[] { param }); - } - } + return reflectionDriver.invoke(findMethod(cls, methodName, argType), null, new Object[] { param }); } catch (Exception e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } + return null; } - return null; } /** From 28b9829f6ae265d26e2fa291b7f590dae00be119 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Sep 2021 02:01:13 +0000 Subject: [PATCH 120/713] Bump jvm-driver from 4.0.0 to 5.1.3 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 4.0.0 to 5.1.3. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-4.0.0...jvm-driver-5.1.3) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e681214c..5b3aa9bbe 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ io.github.toolfactory jvm-driver - 4.0.0 + 5.1.3 From 77d17079f8e791306720f74aca6ad2691b1acbc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 02:00:48 +0000 Subject: [PATCH 121/713] Bump jvm-driver from 5.1.3 to 6.2.2 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 5.1.3 to 6.2.2. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-5.1.3...jvm-driver-6.2.2) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b3aa9bbe..d04b8c59e 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ io.github.toolfactory jvm-driver - 5.1.3 + 6.2.2 From 4c30481ada042af58f7ad03292fc411e276dfc47 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 Oct 2021 14:55:37 -0600 Subject: [PATCH 122/713] Add `requires jvm.driver` (#562) --- pom.xml | 2 +- src/module-info/COMPILING | 3 ++- .../io.github.classgraph/module-info.class | Bin 289 -> 311 bytes .../io.github.classgraph/module-info.java | 6 ++++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index d04b8c59e..29d0a1a76 100644 --- a/pom.xml +++ b/pom.xml @@ -456,7 +456,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc,jvm.driver;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" true diff --git a/src/module-info/COMPILING b/src/module-info/COMPILING index e5dc4e111..c5aaa3d6a 100644 --- a/src/module-info/COMPILING +++ b/src/module-info/COMPILING @@ -1,3 +1,4 @@ # This is compiled separately from ClassGraph, so that ClassGraph can be built on JDK 8 in JDK 7 mode -javac --release 9 io.github.classgraph/module-info.java io.github.classgraph/io/github/classgraph/Placeholder.java +# Replace 6.2.2 with the latest cached version of jvm-driver +javac -p ~/.m2/repository/io/github/toolfactory/jvm-driver/6.2.2/jvm-driver-6.2.2.jar --release 9 io.github.classgraph/module-info.java io.github.classgraph/io/github/classgraph/Placeholder.java diff --git a/src/module-info/io.github.classgraph/module-info.class b/src/module-info/io.github.classgraph/module-info.class index 10722691b0b24652c1e55f7285645545e5fdcd3c..9b65c141eca98021dee00f86e1131fb6420c2b32 100644 GIT binary patch delta 74 zcmZ3;w4I6T)W2Q(7#J8#8N?=XtrZq#5N2fH$|}p%ODW1MODz&%5S_Toh|`LJ4XB2J Tfotz{Nr5T1C-h|`3D4JgLIz&e?gQJ$BPK?uwP698oK B2@e1O diff --git a/src/module-info/io.github.classgraph/module-info.java b/src/module-info/io.github.classgraph/module-info.java index 344d7cadf..d2803f8ec 100644 --- a/src/module-info/io.github.classgraph/module-info.java +++ b/src/module-info/io.github.classgraph/module-info.java @@ -36,6 +36,8 @@ // Compile this in JDK 9 compatibility mode module io.github.classgraph { exports io.github.classgraph; + + // N.B. make sure the "Import-Package" entries in the manifest (in pom.xml) match these "requires" statements // VersionFinder requires java.xml requires java.xml; // FileUtils requires jdk.unsupported (for usage of Unsafe) @@ -44,6 +46,6 @@ requires java.management; // LogNode requires java.logging requires java.logging; - - // N.B. make sure the "Import-Package" entries in the manifest (in pom.xml) match these "requires" statements + // ReflectionUtils requires jvm.driver + requires jvm.driver; } From 27cc057e9c247814fedec30d31bea016def36ed2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 03:28:14 -0600 Subject: [PATCH 123/713] Add ClassGraph.CIRCUMVENT_ENCAPSULATION --- pom.xml | 9 +- .../java/io/github/classgraph/ClassGraph.java | 19 +- .../classgraph/utils/ReflectionUtils.java | 444 ++++++++++++++---- src/module-info/COMPILING | 4 +- .../io.github.classgraph/module-info.class | Bin 311 -> 332 bytes .../io.github.classgraph/module-info.java | 5 +- 6 files changed, 371 insertions(+), 110 deletions(-) diff --git a/pom.xml b/pom.xml index 29d0a1a76..fff79edd1 100644 --- a/pom.xml +++ b/pom.xml @@ -65,11 +65,12 @@ - + io.github.toolfactory - jvm-driver - 6.2.2 + narcissus + 1.0.4 + true @@ -456,7 +457,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc,jvm.driver;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc,io.github.toolfactory.jvm;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" true diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 04fc3a0fc..464e68776 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -80,7 +80,24 @@ public class ClassGraph { Runtime.getRuntime().availableProcessors() * 1.25) // ); - /** If non-null, log while scanning. */ + /** + * If you are running on JDK 16+, the JDK enforces strong encapsulation, and ClassGraph may be unable to read + * the classpath from your classloader if the classloader does not make the classpath available via a public + * method or field. + * + * To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any + * other way, and also include the Narcissus library on + * the classpath or module path. + * + * Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a + * native code library, and is currently only compiled for Linux 32/64 bit, Windows 32/64 bit, and Mac OS X 64 + * bit. + */ + public static final boolean CIRCUMVENT_ENCAPSULATION = false; + + /** + * If non-null, log while scanning. + */ private LogNode topLevelLog; // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 15e103bca..a4c9c91f1 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -28,24 +28,348 @@ */ package nonapi.io.github.classgraph.utils; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import io.github.toolfactory.jvm.DefaultDriver; -import io.github.toolfactory.jvm.Driver; +import io.github.classgraph.ClassGraph; /** Reflection utility methods that can be used by ClassLoaderHandlers. */ public final class ReflectionUtils { + /** The reflection driver to use. */ + private static ReflectionDriver reflectionDriver; + + static { + if (ClassGraph.CIRCUMVENT_ENCAPSULATION) { + try { + reflectionDriver = new NarcissusReflectionDriver(); + } catch (final Throwable t) { + System.err.println("Could not load Narcissus reflection driver: " + t); + // Fall back to standard reflection driver + } + } + if (reflectionDriver == null) { + reflectionDriver = new StandardReflectionDriver(); + } + } + + /** Reflection driver */ + private static abstract class ReflectionDriver { + /** + * Finds a class by name (e.g. {@code "com.xyz.MyClass"}) using the current classloader or the system + * classloader. + * + * @param className + * the class name + * @return the class reference + */ + abstract Class findClass(final String className) throws Exception; + + /** + * Get declared methods for class. + * + * @param cls + * the class + * @return the declared methods + */ + abstract Method[] getDeclaredMethods(Class cls) throws Exception; + + /** + * Get declared constructors for class. + * + * @param + * the generic type + * @param cls + * the class + * @return the declared constructors + */ + abstract Constructor[] getDeclaredConstructors(Class cls) throws Exception; + + /** + * Get declared fields for class. + * + * @param cls + * the class + * @return the declared fields + */ + abstract Field[] getDeclaredFields(Class cls) throws Exception; + + /** + * Get the value of an object field, boxing the value if necessary. + * + * @param object + * the object instance to get the field value from + * @param field + * the non-static field + * @return the value of the field + */ + abstract Object getField(final Object object, final Field field) throws Exception; + + /** + * Get the value of a static field, ignoring visibility and bypassing security checks, boxing the value if + * necessary. + * + * @param field + * the static field + * @return the static field + */ + abstract Object getStaticField(final Field field) throws Exception; + + /** + * Invoke a non-static {@link Object}-return-type method, boxing the result if necessary. + * + * @param object + * the object instance to invoke the method on + * @param method + * the non-static method + * @param args + * the method arguments (or {@code new Object[0]} if there are no args) + * @return the return value (possibly a boxed value) + */ + abstract Object invokeMethod(final Object object, final Method method, final Object... args) + throws Exception; + + /** + * Invoke a static {@link Object}-return-type method, boxing the result if necessary. + * + * @param method + * the static method + * @param args + * the method arguments (or {@code new Object[0]} if there are no args) + * @return the return value (possibly a boxed value) + */ + abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; + + /** Iterator applied to each method of a class and its superclasses/interfaces. */ + private static interface MethodIterator { + /** @return true to stop iterating, or false to continue iterating */ + boolean foundMethod(Method m); + } + + /** + * Iterate through all methods in the given class, ignoring visibility and bypassing security checks. Also + * iterates up through superclasses, to collect all methods of the class and its superclasses. + * + * @param cls + * the class + */ + void forAllMethods(final Class cls, final MethodIterator methodIter) throws Exception { + // Iterate from class to its superclasses, and find initial interfaces to start traversing from + final Set> visited = new HashSet<>(); + final LinkedList> interfaceQueue = new LinkedList<>(); + for (Class c = cls; c != null; c = c.getSuperclass()) { + for (final Method m : getDeclaredMethods(c)) { + if (methodIter.foundMethod(m)) { + return; + } + } + // Find interfaces and superinterfaces implemented by this class or its superclasses + if (c.isInterface() && visited.add(c)) { + interfaceQueue.add(c); + } + for (final Class iface : c.getInterfaces()) { + if (visited.add(iface)) { + interfaceQueue.add(iface); + } + } + } + // Traverse through interfaces looking for default methods + while (!interfaceQueue.isEmpty()) { + final Class iface = interfaceQueue.remove(); + for (final Method m : getDeclaredMethods(iface)) { + if (methodIter.foundMethod(m)) { + return; + } + } + for (final Class superIface : iface.getInterfaces()) { + if (visited.add(superIface)) { + interfaceQueue.add(superIface); + } + } + } + } + + /** + * Find a method by name and parameter types in the given class, ignoring visibility and bypassing security + * checks. + * + * @param cls + * the class + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name + */ + Method findMethod(final Class cls, final String methodName, final Class... paramTypes) + throws Exception { + final AtomicReference method = new AtomicReference<>(); + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + method.set(m); + return true; + } + return false; + } + }); + final Method m = method.get(); + if (m != null) { + return m; + } else { + throw new NoSuchMethodException(methodName); + } + } + } + /** - * Use jvm-driver to bypass Java visibility restrictions, security manager limitations, and strong - * encapsulation. + * Standard reflection driver (uses {@link AccessibleObject#setAccessible(boolean)} to access non-public fields + * if necessary). */ - private static Driver reflectionDriver = new DefaultDriver(); + private static class StandardReflectionDriver extends ReflectionDriver { + @Override + Class findClass(final String className) throws Exception { + return Class.forName(className); + } + + @Override + Method[] getDeclaredMethods(final Class cls) throws Exception { + return cls.getDeclaredMethods(); + } + + @SuppressWarnings("unchecked") + @Override + Constructor[] getDeclaredConstructors(final Class cls) throws Exception { + return (Constructor[]) cls.getDeclaredConstructors(); + } + + @Override + Field[] getDeclaredFields(final Class cls) throws Exception { + return cls.getDeclaredFields(); + } + + private void makeAccessible(final AccessibleObject obj) throws Exception { + if (!obj.isAccessible()) { + try { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + obj.setAccessible(true); + return null; + } + }); + } catch (final Exception e) { + obj.setAccessible(true); + } + } + } + + @Override + Object getField(final Object object, final Field field) throws Exception { + makeAccessible(field); + return field.get(object); + } + + @Override + Object getStaticField(final Field field) throws Exception { + makeAccessible(field); + return field.get(null); + } + + @Override + Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { + makeAccessible(method); + return method.invoke(object, args); + } + + @Override + Object invokeStaticMethod(final Method method, final Object... args) throws Exception { + makeAccessible(method); + return method.invoke(null, args); + } + } + + /** + * Narcissus reflection driver (uses the Narcissus + * library, if it is available, which allows access to non-public fields and methods, circumventing + * encapsulation and visibility controls via JNI). + */ + private static class NarcissusReflectionDriver extends ReflectionDriver { + private final Class narcissusClass; + private final Method getDeclaredMethods; + private final Method findClass; + private final Method getDeclaredConstructors; + private final Method getDeclaredFields; + private final Method getField; + private final Method invokeMethod; + private final Method invokeStaticMethod; + + NarcissusReflectionDriver() throws Exception { + // Access Narcissus via reflection, so that there is no runtime dependency + final StandardReflectionDriver drv = new StandardReflectionDriver(); + narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); + getDeclaredMethods = drv.findMethod(narcissusClass, "getDeclaredMethods"); + findClass = drv.findMethod(narcissusClass, "findClass", String.class); + getDeclaredConstructors = drv.findMethod(narcissusClass, "getDeclaredConstructors"); + getDeclaredFields = drv.findMethod(narcissusClass, "getDeclaredFields"); + getField = drv.findMethod(narcissusClass, "getField", Object.class, Field.class); + invokeMethod = drv.findMethod(narcissusClass, "invokeMethod", Object.class, Method.class, + Object[].class); + invokeStaticMethod = drv.findMethod(narcissusClass, "invokeStaticMethod", Method.class, Object[].class); + } + + @Override + Class findClass(final String className) throws Exception { + return (Class) findClass.invoke(null, className); + } + + @Override + Method[] getDeclaredMethods(final Class cls) throws Exception { + return (Method[]) getDeclaredMethods.invoke(cls); + } + + @SuppressWarnings("unchecked") + @Override + Constructor[] getDeclaredConstructors(final Class cls) throws Exception { + return (Constructor[]) getDeclaredConstructors.invoke(cls); + } + + @Override + Field[] getDeclaredFields(final Class cls) throws Exception { + return (Field[]) getDeclaredFields.invoke(cls); + } + + @Override + Object getField(final Object object, final Field field) throws Exception { + return getField.invoke(object, field); + } + + @Override + Object getStaticField(final Field field) throws Exception { + return findMethod(narcissusClass, "getStaticField", Field.class).invoke(field); + } + + @Override + Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { + return invokeMethod.invoke(object, method, args); + } + + @Override + Object invokeStaticMethod(final Method method, final Object... args) throws Exception { + return invokeStaticMethod.invoke(method, args); + } + } /** * Constructor. @@ -76,13 +400,13 @@ private static Object getFieldVal(final Class cls, final Object obj, final St final boolean throwException) throws IllegalArgumentException { try { for (Class c = cls; c != null; c = c.getSuperclass()) { - for (Field field : reflectionDriver.getDeclaredFields(c)) { + for (final Field field : reflectionDriver.getDeclaredFields(c)) { if (field.getName().equals(fieldName)) { - return reflectionDriver.getFieldValue(obj, field); + return reflectionDriver.getField(obj, field); } } } - } catch (Exception e) { + } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( "Can't read " + (obj == null ? "static " : "") + " field \"" + fieldName + "\": " + e); @@ -147,90 +471,6 @@ public static Object getStaticFieldVal(final Class cls, final String fieldNam return getFieldVal(cls, null, fieldName, throwException); } - /** Iterator applied to each method of a class and its superclasses/interfaces. */ - private static interface MethodIterator { - /** @return true to stop iterating, or false to continue iterating */ - boolean foundMethod(Method m); - } - - /** - * Iterate through all methods in the given class, ignoring visibility and bypassing security checks. Also - * iterates up through superclasses, to collect all methods of the class and its superclasses. - * - * @param cls - * the class - */ - private static void forAllMethods(final Class cls, final MethodIterator methodIter) { - // Iterate from class to its superclasses, and find initial interfaces to start traversing from - final Set> visited = new HashSet<>(); - final LinkedList> interfaceQueue = new LinkedList<>(); - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Method m : reflectionDriver.getDeclaredMethods(c)) { - if (methodIter.foundMethod(m)) { - return; - } - } - // Find interfaces and superinterfaces implemented by this class or its superclasses - if (c.isInterface() && visited.add(c)) { - interfaceQueue.add(c); - } - for (final Class iface : c.getInterfaces()) { - if (visited.add(iface)) { - interfaceQueue.add(iface); - } - } - } - // Traverse through interfaces looking for default methods - while (!interfaceQueue.isEmpty()) { - final Class iface = interfaceQueue.remove(); - for (final Method m : reflectionDriver.getDeclaredMethods(iface)) { - if (methodIter.foundMethod(m)) { - return; - } - } - for (final Class superIface : iface.getInterfaces()) { - if (visited.add(superIface)) { - interfaceQueue.add(superIface); - } - } - } - } - - /** - * Find a method by name and parameter types in the given class, ignoring visibility and bypassing security - * checks. - * - * @param cls - * the class - * @param methodName - * the method name. - * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} - * @throws NoSuchMethodException - * if the class does not contain a method of the given name - */ - private static Method findMethod(final Class cls, final String methodName, final Class... paramTypes) - throws NoSuchMethodException { - final AtomicReference method = new AtomicReference<>(); - forAllMethods(cls, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { - method.set(m); - return true; - } - return false; - } - }); - final Method m = method.get(); - if (m != null) { - return m; - } else { - throw new NoSuchMethodException(methodName); - } - } - /** * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, @@ -257,8 +497,9 @@ public static Object invokeMethod(final Object obj, final String methodName, fin } } try { - return reflectionDriver.invoke(findMethod(obj.getClass(), methodName), obj, new Object[0]); - } catch (Exception e) { + return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName), + new Object[0]); + } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } @@ -296,9 +537,9 @@ public static Object invokeMethod(final Object obj, final String methodName, fin } } try { - return reflectionDriver.invoke(findMethod(obj.getClass(), methodName, argType), obj, - new Object[] { param }); - } catch (Exception e) { + return reflectionDriver.invokeMethod(obj, + reflectionDriver.findMethod(obj.getClass(), methodName, argType), param); + } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } @@ -332,8 +573,8 @@ public static Object invokeStaticMethod(final Class cls, final String methodN } } try { - return reflectionDriver.invoke(findMethod(cls, methodName), null, new Object[0]); - } catch (Exception e) { + return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName)); + } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } @@ -371,8 +612,9 @@ public static Object invokeStaticMethod(final Class cls, final String methodN } } try { - return reflectionDriver.invoke(findMethod(cls, methodName, argType), null, new Object[] { param }); - } catch (Exception e) { + return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName, argType), + param); + } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); } diff --git a/src/module-info/COMPILING b/src/module-info/COMPILING index c5aaa3d6a..8c20e98f9 100644 --- a/src/module-info/COMPILING +++ b/src/module-info/COMPILING @@ -1,4 +1,4 @@ # This is compiled separately from ClassGraph, so that ClassGraph can be built on JDK 8 in JDK 7 mode -# Replace 6.2.2 with the latest cached version of jvm-driver -javac -p ~/.m2/repository/io/github/toolfactory/jvm-driver/6.2.2/jvm-driver-6.2.2.jar --release 9 io.github.classgraph/module-info.java io.github.classgraph/io/github/classgraph/Placeholder.java +# Replace 1.0.4 with the latest cached version of narcissus +javac -p ~/.m2/repository/io/github/toolfactory/narcissus/1.0.4/narcissus-1.0.4.jar --release 9 io.github.classgraph/module-info.java io.github.classgraph/io/github/classgraph/Placeholder.java diff --git a/src/module-info/io.github.classgraph/module-info.class b/src/module-info/io.github.classgraph/module-info.class index 9b65c141eca98021dee00f86e1131fb6420c2b32..9b7f520518d37e08230715d20b15550487ec6c57 100644 GIT binary patch delta 60 zcmdnabcSid9tHW#e7*F{l8n+My^{R=oV3K`lKi4dy}ZPtz^yEePTO diff --git a/src/module-info/io.github.classgraph/module-info.java b/src/module-info/io.github.classgraph/module-info.java index d2803f8ec..f20e2c5da 100644 --- a/src/module-info/io.github.classgraph/module-info.java +++ b/src/module-info/io.github.classgraph/module-info.java @@ -46,6 +46,7 @@ requires java.management; // LogNode requires java.logging requires java.logging; - // ReflectionUtils requires jvm.driver - requires jvm.driver; + + // ReflectionUtils may use io.github.toolfactory.narcissus if it is available + requires static io.github.toolfactory.narcissus; } From d74bce426634963acdab066b05073762ac14cbe9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 03:29:48 -0600 Subject: [PATCH 124/713] Fix Javadoc --- src/main/java/io/github/classgraph/ClassGraph.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 464e68776..e7bdae1b2 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -85,11 +85,11 @@ public class ClassGraph { * the classpath from your classloader if the classloader does not make the classpath available via a public * method or field. * - * To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any + *

To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any * other way, and also include the Narcissus library on * the classpath or module path. * - * Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a + *

Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a * native code library, and is currently only compiled for Linux 32/64 bit, Windows 32/64 bit, and Mac OS X 64 * bit. */ From a0cd8ba4cbda0a9f0e4b4b939c33a9afdda801ab Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 03:30:09 -0600 Subject: [PATCH 125/713] [maven-release-plugin] prepare release classgraph-4.8.118 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fff79edd1..2c78c842f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.118-SNAPSHOT + 4.8.118 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.105 + classgraph-4.8.118 From 0ceaaf1b6ed35f50b2c71bf2b8492cecc146c4a7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 03:30:11 -0600 Subject: [PATCH 126/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c78c842f..627c8e552 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.118 + 4.8.119-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 0671965a02e20aeda8474edb5584995665277f35 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 03:47:16 -0600 Subject: [PATCH 127/713] Update Javadoc --- src/main/java/io/github/classgraph/ClassGraph.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index e7bdae1b2..9b2b55bca 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -85,13 +85,14 @@ public class ClassGraph { * the classpath from your classloader if the classloader does not make the classpath available via a public * method or field. * - *

To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any + *

+ * To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any * other way, and also include the Narcissus library on * the classpath or module path. * - *

Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a - * native code library, and is currently only compiled for Linux 32/64 bit, Windows 32/64 bit, and Mac OS X 64 - * bit. + *

+ * Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a native + * code library, and is currently only compiled for Linux x86/x64, Windows x86/x64, and Mac OS X x64 bit. */ public static final boolean CIRCUMVENT_ENCAPSULATION = false; From a2b428f10fde8957a4018c002c465f69e3139d5b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 05:10:36 -0600 Subject: [PATCH 128/713] Make CIRCUMVENT_ENCAPSULATION non-final --- src/main/java/io/github/classgraph/ClassGraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 9b2b55bca..f655baa5e 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -94,7 +94,7 @@ public class ClassGraph { * Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a native * code library, and is currently only compiled for Linux x86/x64, Windows x86/x64, and Mac OS X x64 bit. */ - public static final boolean CIRCUMVENT_ENCAPSULATION = false; + public static boolean CIRCUMVENT_ENCAPSULATION = false; /** * If non-null, log while scanning. From 62b7bb114c88115d31b801bc159633121475f36e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 05:11:25 -0600 Subject: [PATCH 129/713] [maven-release-plugin] prepare release classgraph-4.8.119 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 627c8e552..7254f279d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.119-SNAPSHOT + 4.8.119 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.119 From de546bb947f70e51e1ada522bf97869d805fe0b9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 05:11:27 -0600 Subject: [PATCH 130/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7254f279d..183e2b858 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.119 + 4.8.120-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.119 + classgraph-4.8.118 From b80f5779bd69fb00a85913393faa7081d9c1c08d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:09:57 -0600 Subject: [PATCH 131/713] Fix ReflectionUtils issues --- .../classgraph/utils/ReflectionUtils.java | 72 ++++++++++++++----- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index a4c9c91f1..417ff781a 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -148,10 +148,19 @@ abstract Object invokeMethod(final Object object, final Method method, final Obj */ abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; + /** + * Make a field or method accessible. + * + * @param accessibleObject + * the field or method. + * @return true if successful. + */ + abstract boolean makeAccessible(final AccessibleObject accessibleObject); + /** Iterator applied to each method of a class and its superclasses/interfaces. */ private static interface MethodIterator { /** @return true to stop iterating, or false to continue iterating */ - boolean foundMethod(Method m); + boolean foundMethod(Method m) throws Exception; } /** @@ -167,8 +176,13 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E final LinkedList> interfaceQueue = new LinkedList<>(); for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Method m : getDeclaredMethods(c)) { - if (methodIter.foundMethod(m)) { - return; + try { + if (methodIter.foundMethod(m)) { + return; + } + } catch (IllegalAccessException | SecurityException e) { + // Fall through to keep looking for method in superclass, if subclass throws an access + // exception } } // Find interfaces and superinterfaces implemented by this class or its superclasses @@ -185,8 +199,13 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E while (!interfaceQueue.isEmpty()) { final Class iface = interfaceQueue.remove(); for (final Method m : getDeclaredMethods(iface)) { - if (methodIter.foundMethod(m)) { - return; + try { + if (methodIter.foundMethod(m)) { + return; + } + } catch (IllegalAccessException | SecurityException e) { + // Fall through to keep looking for method in superclass, if subclass throws an access + // exception } } for (final Class superIface : iface.getInterfaces()) { @@ -217,7 +236,10 @@ Method findMethod(final Class cls, final String methodName, final Class... forAllMethods(cls, new MethodIterator() { @Override public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes()) + // If method is not accessible, fall through and try superclass method of same + // name and paramTypes + && makeAccessible(m)) { method.set(m); return true; } @@ -259,7 +281,7 @@ Field[] getDeclaredFields(final Class cls) throws Exception { return cls.getDeclaredFields(); } - private void makeAccessible(final AccessibleObject obj) throws Exception { + boolean makeAccessible(final AccessibleObject obj) { if (!obj.isAccessible()) { try { AccessController.doPrivileged(new PrivilegedAction() { @@ -269,10 +291,15 @@ public Void run() { return null; } }); - } catch (final Exception e) { - obj.setAccessible(true); + } catch (final Throwable e) { + try { + obj.setAccessible(true); + } catch (final Throwable e2) { + return false; + } } } + return obj.isAccessible(); } @Override @@ -312,6 +339,7 @@ private static class NarcissusReflectionDriver extends ReflectionDriver { private final Method getDeclaredConstructors; private final Method getDeclaredFields; private final Method getField; + private final Method getStaticField; private final Method invokeMethod; private final Method invokeStaticMethod; @@ -319,16 +347,22 @@ private static class NarcissusReflectionDriver extends ReflectionDriver { // Access Narcissus via reflection, so that there is no runtime dependency final StandardReflectionDriver drv = new StandardReflectionDriver(); narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); - getDeclaredMethods = drv.findMethod(narcissusClass, "getDeclaredMethods"); findClass = drv.findMethod(narcissusClass, "findClass", String.class); - getDeclaredConstructors = drv.findMethod(narcissusClass, "getDeclaredConstructors"); - getDeclaredFields = drv.findMethod(narcissusClass, "getDeclaredFields"); + getDeclaredMethods = drv.findMethod(narcissusClass, "getDeclaredMethods", Class.class); + getDeclaredConstructors = drv.findMethod(narcissusClass, "getDeclaredConstructors", Class.class); + getDeclaredFields = drv.findMethod(narcissusClass, "getDeclaredFields", Class.class); getField = drv.findMethod(narcissusClass, "getField", Object.class, Field.class); + getStaticField = drv.findMethod(narcissusClass, "getStaticField", Field.class); invokeMethod = drv.findMethod(narcissusClass, "invokeMethod", Object.class, Method.class, Object[].class); invokeStaticMethod = drv.findMethod(narcissusClass, "invokeStaticMethod", Method.class, Object[].class); } + @Override + boolean makeAccessible(AccessibleObject accessibleObject) { + return true; + } + @Override Class findClass(final String className) throws Exception { return (Class) findClass.invoke(null, className); @@ -336,38 +370,38 @@ Class findClass(final String className) throws Exception { @Override Method[] getDeclaredMethods(final Class cls) throws Exception { - return (Method[]) getDeclaredMethods.invoke(cls); + return (Method[]) getDeclaredMethods.invoke(null, cls); } @SuppressWarnings("unchecked") @Override Constructor[] getDeclaredConstructors(final Class cls) throws Exception { - return (Constructor[]) getDeclaredConstructors.invoke(cls); + return (Constructor[]) getDeclaredConstructors.invoke(null, cls); } @Override Field[] getDeclaredFields(final Class cls) throws Exception { - return (Field[]) getDeclaredFields.invoke(cls); + return (Field[]) getDeclaredFields.invoke(null, cls); } @Override Object getField(final Object object, final Field field) throws Exception { - return getField.invoke(object, field); + return getField.invoke(null, object, field); } @Override Object getStaticField(final Field field) throws Exception { - return findMethod(narcissusClass, "getStaticField", Field.class).invoke(field); + return getStaticField.invoke(null, field); } @Override Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(object, method, args); + return invokeMethod.invoke(null, object, method, args); } @Override Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - return invokeStaticMethod.invoke(method, args); + return invokeStaticMethod.invoke(null, method, args); } } From 43c97c01e4b2185e81c68b4a2ef05728b7aab27e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:10:23 -0600 Subject: [PATCH 132/713] [maven-release-plugin] prepare release classgraph-4.8.120 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 183e2b858..9343793f0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.120-SNAPSHOT + 4.8.120 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.120 From 94237c495af7291d7f9eb04bcfbf09e100dc70b1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:10:26 -0600 Subject: [PATCH 133/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9343793f0..6075c51dd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.120 + 4.8.121-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.120 + classgraph-4.8.118 From d841f638a23faeb81dd43270887b1cceef29e2a9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:33:38 -0600 Subject: [PATCH 134/713] Enable -parameters compiler switch: it's default in JDK17/win (#562) --- pom.xml | 22 ++++++++++++++----- .../features/DeclaredVsNonDeclaredTest.java | 8 ++++--- .../issues/issue151/Issue151Test.java | 6 ++--- .../issues/issue152/Issue152Test.java | 16 +++++++++----- .../issues/issue402/TypeAnnotationTest.java | 4 ++-- .../test/methodinfo/MethodInfoTest.java | 18 ++++++++------- 6 files changed, 47 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 6075c51dd..e9346d1cb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -72,7 +75,7 @@ 1.0.4 true - + org.junit.jupiter @@ -276,7 +279,8 @@ - + org.codehaus.mojo.signature java17 @@ -355,8 +359,13 @@ - - + + @@ -387,6 +396,9 @@ 8 8 false + + -parameters + diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index d571b0a23..4e81fbc35 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -128,9 +128,11 @@ public void declaredVsNonDeclaredMethods() { assertThat(B.getFieldInfo("z").getClassInfo().getName()).isEqualTo(A.class.getName()); assertThat(A.getFieldInfo().get(0).getTypeDescriptor().toString()).isEqualTo("float"); assertThat(B.getFieldInfo().get(0).getTypeDescriptor().toString()).isEqualTo("int"); - assertThat(B.getMethodInfo().toString()).isEqualTo( - "[void y(int, int), void w(), abstract void y(java.lang.String), abstract void y(java.lang.Integer)]"); - assertThat(B.getDeclaredMethodInfo().toString()).isEqualTo("[void y(int, int), void w()]"); + assertThat(B.getMethodInfo().toString()) + .isEqualTo("[void y(final int x, final int y), void w(), abstract void y(java.lang.String x), " + + "abstract void y(java.lang.Integer x)]"); + assertThat(B.getDeclaredMethodInfo().toString()) + .isEqualTo("[void y(final int x, final int y), void w()]"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java b/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java index 89508258f..ee90e9f9a 100644 --- a/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java +++ b/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java @@ -62,9 +62,9 @@ public void issue151Test() { .getMethodInfo("method") // .get(0); assertThat(methodInfo.toString()) // - .isEqualTo("public void method(@" + ParamAnnotation0.class.getName() + " java.lang.String, @" - + ParamAnnotation1.class.getName() + " @" + ParamAnnotation2.class.getName() - + " java.lang.String)"); + .isEqualTo("public void method(@" + ParamAnnotation0.class.getName() + + " final java.lang.String annotatedValue0, @" + ParamAnnotation1.class.getName() + " @" + + ParamAnnotation2.class.getName() + " final java.lang.String annotatedValue1)"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java index bc4b6abf0..17c4b1be5 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -98,12 +98,16 @@ public void issue152Test() { .getMethodInfo("testMethod") // .get(0).toString()) // .isEqualTo("public java.util.Set testMethod(" - + "java.util.List, java.util.Map>, double[][][], int, " - + TestType.class.getName().replace('$', '.') + "[], java.util.Set, java.util.List, java.util.Map, java.util.Set[])"); + + "final java.util.List param0, " + + "final java.util.Map> param2, " + + "final double[][][] param3, final int param4, final " + + TestType.class.getName().replace('$', '.') + "[] param5, " + + "final java.util.Set param6, " + "final java.util.List param7, " + + "final java.util.Map param8, " + + "final java.util.Set[] param9)"); assertThat(classInfo // .getFieldInfo("testField").toString()) // .isEqualTo("public java.util.Map xyz4"); assertThat(shortNames(classInfo.getMethodInfo("t").get(0))) - .isEqualTo("<@A T extends @B U> @D U t(@E T)"); + .isEqualTo("<@A T extends @B U> @D U t(final @E T t)"); assertThat(classInfo.getMethodInfo("t").get(0).toStringWithSimpleNames()) - .isEqualTo("<@A T extends @B U> @D U t(@E T)"); + .isEqualTo("<@A T extends @B U> @D U t(final @E T t)"); final ClassInfo personClassInfo = scanResult.getClassInfo(Person.class.getName()); diff --git a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java index 2a722c2f3..5c1316606 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -129,10 +129,11 @@ public boolean accept(final MethodInfo methodInfo) { } }).getAsStrings()).containsOnly( // "@" + ExternalAnnotation.class.getName() // - + " public final int publicMethodWithArgs" - + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, " + X.class.getName().replace('$', '.') - + "[][][], java.lang.String[]...)", + + " public final int publicMethodWithArgs(final java.lang.String str, " + + "final char c, final long j, final float[] f, final byte[][] b, " + + "final java.util.List l, " + + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " + + "final java.lang.String[]... varargs)", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", "@" + Test.class.getName() + " public void testGetConstructorInfo()", @@ -170,10 +171,11 @@ public boolean accept(final MethodInfo methodInfo) { } }).getAsStrings()).containsOnly( // "@" + ExternalAnnotation.class.getName() // - + " public final int publicMethodWithArgs" - + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, " + X.class.getName().replace('$', '.') - + "[][][], java.lang.String[]...)", + + " public final int publicMethodWithArgs(final java.lang.String str, " + + "final char c, final long j, final float[] f, final byte[][] b, " + + "final java.util.List l, " + + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " + + "final java.lang.String[]... varargs)", "private static java.lang.String[] privateMethod()", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", From d10cc526315487fc149e1e0804f419a383e9a752 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:40:20 -0600 Subject: [PATCH 135/713] Update README --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 8a35c4d44..14e711742 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,32 @@ Replace `X.Y.Z` below with the latest [release number](https://github.com/classg See instructions for [use as a module](https://github.com/classgraph/classgraph/wiki#use-as-a-module). +### Running on JDK 16+ + +The JDK team decided to start enforcing strong encapsulation in JDK 16+. That will means that by default, ClassGraph will not be able to find the classpath of your project, if all of the following are true: + +* You are running on JDK 16+ +* You are using a legacy classloader (rather than the module system) +* The legacy classloader does not expose its classpath via a public field or method +* The classloader is loaded in a different module from your user code + +**If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem.** + +You can circumvent this restriction by: + +* Upgrading ClassGraph to at least version 4.8.120 +* Adding the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). +* Setting `ClassGraph.CIRCUMVENT_ENCAPSULATION = true;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). + +ClassGraph uses Narcissus to silently circumvent all of Java's security mechanisms (visibility/access checks, security manager restrictions, and strong encapsulation) by using the JNI API, in order to read the classpath from private fields and methods of classloaders. + +Narcissus is a collaboration between: + +* Luke Hutchison (@lukehutch), author of ClassGraph +* Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core) and [toolfactory/jvm-driver](https://github.com/toolfactory/jvm-driver), which is an alternative to Narcissus + +JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** + ### Pre-built JARs You can get pre-built JARs (usable on JRE 7 or newer) from [Sonatype](https://oss.sonatype.org/#nexus-search;quick~io.github.classgraph). From 65c55252b6b511404a02bf73cf455c83803b19ba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:44:12 -0600 Subject: [PATCH 136/713] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14e711742..3c6c61fa3 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Narcissus is a collaboration between: * Luke Hutchison (@lukehutch), author of ClassGraph * Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core) and [toolfactory/jvm-driver](https://github.com/toolfactory/jvm-driver), which is an alternative to Narcissus -JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** +JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment maintainers to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** ### Pre-built JARs From 24272872cd79d8552bb496e6491c8e1d19c4d9f0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 06:58:27 -0600 Subject: [PATCH 137/713] Remove unnecessary code --- .../classgraph/utils/ReflectionUtils.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 417ff781a..68136ce98 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -176,13 +176,8 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E final LinkedList> interfaceQueue = new LinkedList<>(); for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Method m : getDeclaredMethods(c)) { - try { - if (methodIter.foundMethod(m)) { - return; - } - } catch (IllegalAccessException | SecurityException e) { - // Fall through to keep looking for method in superclass, if subclass throws an access - // exception + if (methodIter.foundMethod(m)) { + return; } } // Find interfaces and superinterfaces implemented by this class or its superclasses @@ -199,13 +194,8 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E while (!interfaceQueue.isEmpty()) { final Class iface = interfaceQueue.remove(); for (final Method m : getDeclaredMethods(iface)) { - try { - if (methodIter.foundMethod(m)) { - return; - } - } catch (IllegalAccessException | SecurityException e) { - // Fall through to keep looking for method in superclass, if subclass throws an access - // exception + if (methodIter.foundMethod(m)) { + return; } } for (final Class superIface : iface.getInterfaces()) { From fe9c66926dfe3f42acd950828ac857e43f15d65a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 07:00:50 -0600 Subject: [PATCH 138/713] Small optimization --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 68136ce98..230c49fb6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -288,8 +288,9 @@ public Void run() { return false; } } + return obj.isAccessible(); } - return obj.isAccessible(); + return true; } @Override From 05c20e9ce30417df294c3ac3ebdbb13e57f09428 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 07:09:21 -0600 Subject: [PATCH 139/713] Fix for reading static fields; refactoring --- .../classgraph/utils/ReflectionUtils.java | 87 ++++++++++--------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 230c49fb6..6f1c0c2f9 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -218,7 +218,7 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E * the parameter types of the method. * @return the {@link Method} * @throws NoSuchMethodException - * if the class does not contain a method of the given name + * if the class does not contain a method of the given name and parameter types */ Method findMethod(final Class cls, final String methodName, final Class... paramTypes) throws Exception { @@ -243,6 +243,28 @@ && makeAccessible(m)) { throw new NoSuchMethodException(methodName); } } + + /** + * Find a field by name in the given class, ignoring visibility and bypassing security checks. + * + * @param cls + * the class + * @param fieldName + * the field name. + * @return the {@link Field} + * @throws NoSuchFieldException + * if the class does not contain a field of the given name + */ + Field findField(final Class cls, final String fieldName) throws Exception { + for (Class c = cls; c != null; c = c.getSuperclass()) { + for (final Field field : getDeclaredFields(c)) { + if (field.getName().equals(fieldName)) { + return field; + } + } + } + throw new NoSuchFieldException(fieldName); + } } /** @@ -403,43 +425,6 @@ private ReflectionUtils() { // Cannot be constructed } - /** - * Get the value of the named field in the class of the given object or any of its superclasses. If an exception - * is thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown - * wrapping the cause, otherwise this will return null. If passed a null object, returns null unless - * throwException is true, then throws IllegalArgumentException. - * - * @param cls - * The class. - * @param obj - * The object, or null to get the value of a static field. - * @param fieldName - * The field name. - * @param throwException - * If true, throw an exception if the field value could not be read. - * @return The field value. - * @throws IllegalArgumentException - * If the field value could not be read. - */ - private static Object getFieldVal(final Class cls, final Object obj, final String fieldName, - final boolean throwException) throws IllegalArgumentException { - try { - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Field field : reflectionDriver.getDeclaredFields(c)) { - if (field.getName().equals(fieldName)) { - return reflectionDriver.getField(obj, field); - } - } - } - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException( - "Can't read " + (obj == null ? "static " : "") + " field \"" + fieldName + "\": " + e); - } - } - return null; - } - /** * Get the value of the named field in the class of the given object or any of its superclasses. If an exception * is thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown @@ -465,7 +450,15 @@ public static Object getFieldVal(final Object obj, final String fieldName, final return null; } } - return getFieldVal(obj.getClass(), obj, fieldName, throwException); + try { + return reflectionDriver.getField(obj, reflectionDriver.findField(obj.getClass(), fieldName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Can't read field " + obj.getClass().getName() + "." + fieldName + ": " + e); + } + } + return null; } /** @@ -493,7 +486,15 @@ public static Object getStaticFieldVal(final Class cls, final String fieldNam return null; } } - return getFieldVal(cls, null, fieldName, throwException); + try { + return reflectionDriver.getStaticField(reflectionDriver.findField(cls, fieldName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Can't read static field " + cls.getName() + "." + fieldName + ": " + e); + } + } + return null; } /** @@ -601,7 +602,8 @@ public static Object invokeStaticMethod(final Class cls, final String methodN return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException( + "Static method \"" + methodName + "\" could not be invoked: " + e); } return null; } @@ -641,7 +643,8 @@ public static Object invokeStaticMethod(final Class cls, final String methodN param); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException( + "Static method \"" + methodName + "\" could not be invoked: " + e); } return null; } From 1aaaba79e459e3402f91876aad497614be80eb3f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 07:12:06 -0600 Subject: [PATCH 140/713] Fix Javadoc --- .../classgraph/utils/ReflectionUtils.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 6f1c0c2f9..459cc6977 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -64,8 +64,7 @@ public final class ReflectionUtils { /** Reflection driver */ private static abstract class ReflectionDriver { /** - * Finds a class by name (e.g. {@code "com.xyz.MyClass"}) using the current classloader or the system - * classloader. + * Find a class by name. * * @param className * the class name @@ -114,8 +113,7 @@ private static abstract class ReflectionDriver { abstract Object getField(final Object object, final Field field) throws Exception; /** - * Get the value of a static field, ignoring visibility and bypassing security checks, boxing the value if - * necessary. + * Get the value of a static field, boxing the value if necessary. * * @param field * the static field @@ -124,7 +122,7 @@ private static abstract class ReflectionDriver { abstract Object getStaticField(final Field field) throws Exception; /** - * Invoke a non-static {@link Object}-return-type method, boxing the result if necessary. + * Invoke a non-static method, boxing the result if necessary. * * @param object * the object instance to invoke the method on @@ -138,7 +136,7 @@ abstract Object invokeMethod(final Object object, final Method method, final Obj throws Exception; /** - * Invoke a static {@link Object}-return-type method, boxing the result if necessary. + * Invoke a static method, boxing the result if necessary. * * @param method * the static method @@ -164,8 +162,8 @@ private static interface MethodIterator { } /** - * Iterate through all methods in the given class, ignoring visibility and bypassing security checks. Also - * iterates up through superclasses, to collect all methods of the class and its superclasses. + * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to + * collect all methods of the class and its superclasses, and any default methods defined in interfaces. * * @param cls * the class @@ -207,8 +205,7 @@ void forAllMethods(final Class cls, final MethodIterator methodIter) throws E } /** - * Find a method by name and parameter types in the given class, ignoring visibility and bypassing security - * checks. + * Find a method by name and parameter types in the given class. * * @param cls * the class @@ -245,7 +242,7 @@ && makeAccessible(m)) { } /** - * Find a field by name in the given class, ignoring visibility and bypassing security checks. + * Find a field by name in the given class. * * @param cls * the class From e7cf33c7b20022371d5b1793a8ef9f1cd14bfe21 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 07:17:24 -0600 Subject: [PATCH 141/713] Add field setters for completeness --- .../classgraph/utils/ReflectionUtils.java | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 459cc6977..4cb6358df 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -102,7 +102,7 @@ private static abstract class ReflectionDriver { abstract Field[] getDeclaredFields(Class cls) throws Exception; /** - * Get the value of an object field, boxing the value if necessary. + * Get the value of a non-static field, boxing the value if necessary. * * @param object * the object instance to get the field value from @@ -121,6 +121,28 @@ private static abstract class ReflectionDriver { */ abstract Object getStaticField(final Field field) throws Exception; + /** + * Set the value of a non-static field, unboxing the value if necessary. + * + * @param object + * the object instance to get the field value from + * @param field + * the non-static field + * @param value + * the value to set + */ + abstract void setField(final Object object, final Field field, Object value) throws Exception; + + /** + * Set the value of a static field, unboxing the value if necessary. + * + * @param field + * the static field + * @param the + * value to set + */ + abstract void setStaticField(final Field field, Object value) throws Exception; + /** * Invoke a non-static method, boxing the result if necessary. * @@ -324,6 +346,18 @@ Object getStaticField(final Field field) throws Exception { return field.get(null); } + @Override + void setField(final Object object, final Field field, Object value) throws Exception { + makeAccessible(field); + field.set(object, value); + } + + @Override + void setStaticField(final Field field, Object value) throws Exception { + makeAccessible(field); + field.set(null, value); + } + @Override Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { makeAccessible(method); @@ -350,6 +384,8 @@ private static class NarcissusReflectionDriver extends ReflectionDriver { private final Method getDeclaredFields; private final Method getField; private final Method getStaticField; + private final Method setField; + private final Method setStaticField; private final Method invokeMethod; private final Method invokeStaticMethod; @@ -363,6 +399,8 @@ private static class NarcissusReflectionDriver extends ReflectionDriver { getDeclaredFields = drv.findMethod(narcissusClass, "getDeclaredFields", Class.class); getField = drv.findMethod(narcissusClass, "getField", Object.class, Field.class); getStaticField = drv.findMethod(narcissusClass, "getStaticField", Field.class); + setField = drv.findMethod(narcissusClass, "setField", Object.class, Field.class, Object.class); + setStaticField = drv.findMethod(narcissusClass, "getStaticField", Field.class, Object.class); invokeMethod = drv.findMethod(narcissusClass, "invokeMethod", Object.class, Method.class, Object[].class); invokeStaticMethod = drv.findMethod(narcissusClass, "invokeStaticMethod", Method.class, Object[].class); @@ -404,6 +442,16 @@ Object getStaticField(final Field field) throws Exception { return getStaticField.invoke(null, field); } + @Override + void setField(final Object object, final Field field, Object value) throws Exception { + setField.invoke(null, object, field, value); + } + + @Override + void setStaticField(final Field field, Object value) throws Exception { + setStaticField.invoke(null, field, value); + } + @Override Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { return invokeMethod.invoke(null, object, method, args); From ac3f369b079b3b65182edad6f46c92e31c419038 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Oct 2021 19:55:45 -0600 Subject: [PATCH 142/713] Ensure Narcissus native library is loaded --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 4cb6358df..b3cde8222 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -393,6 +393,9 @@ private static class NarcissusReflectionDriver extends ReflectionDriver { // Access Narcissus via reflection, so that there is no runtime dependency final StandardReflectionDriver drv = new StandardReflectionDriver(); narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); + if (!(Boolean) drv.getStaticField(drv.findField(narcissusClass, "libraryLoaded"))) { + throw new IllegalArgumentException("Could not load Narcissus native library"); + } findClass = drv.findMethod(narcissusClass, "findClass", String.class); getDeclaredMethods = drv.findMethod(narcissusClass, "getDeclaredMethods", Class.class); getDeclaredConstructors = drv.findMethod(narcissusClass, "getDeclaredConstructors", Class.class); From d859088a5d2d200b00fcc6bc17bf8d630e2ec5b8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 05:07:12 -0600 Subject: [PATCH 143/713] Improve reflection code --- .../classgraph/utils/ReflectionUtils.java | 118 +++++++++++++----- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index b3cde8222..c1894d404 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -34,9 +34,13 @@ import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -112,15 +116,6 @@ private static abstract class ReflectionDriver { */ abstract Object getField(final Object object, final Field field) throws Exception; - /** - * Get the value of a static field, boxing the value if necessary. - * - * @param field - * the static field - * @return the static field - */ - abstract Object getStaticField(final Field field) throws Exception; - /** * Set the value of a non-static field, unboxing the value if necessary. * @@ -133,6 +128,15 @@ private static abstract class ReflectionDriver { */ abstract void setField(final Object object, final Field field, Object value) throws Exception; + /** + * Get the value of a static field, boxing the value if necessary. + * + * @param field + * the static field + * @return the static field + */ + abstract Object getStaticField(final Field field) throws Exception; + /** * Set the value of a static field, unboxing the value if necessary. * @@ -263,6 +267,26 @@ && makeAccessible(m)) { } } + /** + * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also + * iterates up through superclasses, to collect all methods of the class and its superclasses. + * + * @param cls + * the class + * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. + */ + List enumerateMethods(final Class cls) throws Exception { + final List methodOrder = new ArrayList<>(); + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + methodOrder.add(m); + return false; + } + }); + return methodOrder; + } + /** * Find a field by name in the given class. * @@ -312,6 +336,7 @@ Field[] getDeclaredFields(final Class cls) throws Exception { return cls.getDeclaredFields(); } + @Override boolean makeAccessible(final AccessibleObject obj) { if (!obj.isAccessible()) { try { @@ -341,19 +366,19 @@ Object getField(final Object object, final Field field) throws Exception { } @Override - Object getStaticField(final Field field) throws Exception { + void setField(final Object object, final Field field, final Object value) throws Exception { makeAccessible(field); - return field.get(null); + field.set(object, value); } @Override - void setField(final Object object, final Field field, Object value) throws Exception { + Object getStaticField(final Field field) throws Exception { makeAccessible(field); - field.set(object, value); + return field.get(null); } @Override - void setStaticField(final Field field, Object value) throws Exception { + void setStaticField(final Field field, final Object value) throws Exception { makeAccessible(field); field.set(null, value); } @@ -377,40 +402,67 @@ Object invokeStaticMethod(final Method method, final Object... args) throws Exce * encapsulation and visibility controls via JNI). */ private static class NarcissusReflectionDriver extends ReflectionDriver { + private final Map> methodNameToMethods = new HashMap<>(); private final Class narcissusClass; private final Method getDeclaredMethods; private final Method findClass; private final Method getDeclaredConstructors; private final Method getDeclaredFields; private final Method getField; - private final Method getStaticField; private final Method setField; + private final Method getStaticField; private final Method setStaticField; private final Method invokeMethod; private final Method invokeStaticMethod; + private Method findIndexedMethod(final String methodName, final Class... paramTypes) + throws NoSuchMethodException { + final List methods = methodNameToMethods.get(methodName); + if (methods != null) { + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + return method; + } + } + } + throw new NoSuchMethodException(methodName); + } + + private void indexMethods(final List methods) { + // Index Narcissus methods by name + for (final Method method : methods) { + List methodsForName = methodNameToMethods.get(method.getName()); + if (methodsForName == null) { + methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); + } + methodsForName.add(method); + } + } + NarcissusReflectionDriver() throws Exception { - // Access Narcissus via reflection, so that there is no runtime dependency + // Load Narcissus class via reflection, so that there is no runtime dependency final StandardReflectionDriver drv = new StandardReflectionDriver(); narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); if (!(Boolean) drv.getStaticField(drv.findField(narcissusClass, "libraryLoaded"))) { throw new IllegalArgumentException("Could not load Narcissus native library"); } - findClass = drv.findMethod(narcissusClass, "findClass", String.class); - getDeclaredMethods = drv.findMethod(narcissusClass, "getDeclaredMethods", Class.class); - getDeclaredConstructors = drv.findMethod(narcissusClass, "getDeclaredConstructors", Class.class); - getDeclaredFields = drv.findMethod(narcissusClass, "getDeclaredFields", Class.class); - getField = drv.findMethod(narcissusClass, "getField", Object.class, Field.class); - getStaticField = drv.findMethod(narcissusClass, "getStaticField", Field.class); - setField = drv.findMethod(narcissusClass, "setField", Object.class, Field.class, Object.class); - setStaticField = drv.findMethod(narcissusClass, "getStaticField", Field.class, Object.class); - invokeMethod = drv.findMethod(narcissusClass, "invokeMethod", Object.class, Method.class, - Object[].class); - invokeStaticMethod = drv.findMethod(narcissusClass, "invokeStaticMethod", Method.class, Object[].class); + + // Look up needed methods + indexMethods(drv.enumerateMethods(narcissusClass)); + findClass = findIndexedMethod("findClass", String.class); + getDeclaredMethods = findIndexedMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findIndexedMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findIndexedMethod("getDeclaredFields", Class.class); + getField = findIndexedMethod("getField", Object.class, Field.class); + setField = findIndexedMethod("setField", Object.class, Field.class, Object.class); + getStaticField = findIndexedMethod("getStaticField", Field.class); + setStaticField = findIndexedMethod("setStaticField", Field.class, Object.class); + invokeMethod = findIndexedMethod("invokeMethod", Object.class, Method.class, Object[].class); + invokeStaticMethod = findIndexedMethod("invokeStaticMethod", Method.class, Object[].class); } @Override - boolean makeAccessible(AccessibleObject accessibleObject) { + boolean makeAccessible(final AccessibleObject accessibleObject) { return true; } @@ -441,17 +493,17 @@ Object getField(final Object object, final Field field) throws Exception { } @Override - Object getStaticField(final Field field) throws Exception { - return getStaticField.invoke(null, field); + void setField(final Object object, final Field field, final Object value) throws Exception { + setField.invoke(null, object, field, value); } @Override - void setField(final Object object, final Field field, Object value) throws Exception { - setField.invoke(null, object, field, value); + Object getStaticField(final Field field) throws Exception { + return getStaticField.invoke(null, field); } @Override - void setStaticField(final Field field, Object value) throws Exception { + void setStaticField(final Field field, final Object value) throws Exception { setStaticField.invoke(null, field, value); } From b2141feca621f06d6745d97fbbf07e3701dc319b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 05:08:04 -0600 Subject: [PATCH 144/713] [maven-release-plugin] prepare release classgraph-4.8.121 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index e9346d1cb..8ef42746c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.121-SNAPSHOT + 4.8.121 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.121 @@ -279,8 +276,7 @@ - + org.codehaus.mojo.signature java17 @@ -359,13 +355,8 @@ - - + + From 4d5ecc90a3a3e9403e1b6059edde6f664ca27278 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 05:08:07 -0600 Subject: [PATCH 145/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8ef42746c..c5ee315be 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.121 + 4.8.122-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.121 + classgraph-4.8.118 From 983db35081ceaee38d0223c85e66fb332adf59b1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 05:20:22 -0600 Subject: [PATCH 146/713] Fix method so that it uses reflection driver --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index c1894d404..88ec8f4df 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -759,8 +759,8 @@ public static Object invokeStaticMethod(final Class cls, final String methodN */ public static Class classForNameOrNull(final String className) { try { - return Class.forName(className); - } catch (final ReflectiveOperationException | LinkageError e) { + return reflectionDriver.findClass(className); + } catch (final Throwable e) { return null; } } From 69283932c27198207f2099ab35e4c1926efa1150 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 05:23:01 -0600 Subject: [PATCH 147/713] Small code cleanup --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 88ec8f4df..27cccd0fc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -623,8 +623,7 @@ public static Object invokeMethod(final Object obj, final String methodName, fin } } try { - return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName), - new Object[0]); + return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName)); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); From 39a611f3116d7bf6f54c636f7b37ec7610564015 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 17:21:27 -0600 Subject: [PATCH 148/713] Bump Narcissus version to 1.0.5; add test --- pom.xml | 2 +- .../classgraph/features/NarcissusTest.java | 28 +++++++++++++++++++ .../issues/issue420/Issue420Test.java | 3 ++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/github/classgraph/features/NarcissusTest.java diff --git a/pom.xml b/pom.xml index c5ee315be..8464f021c 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ io.github.toolfactory narcissus - 1.0.4 + 1.0.5 true diff --git a/src/test/java/io/github/classgraph/features/NarcissusTest.java b/src/test/java/io/github/classgraph/features/NarcissusTest.java new file mode 100644 index 000000000..331d92a87 --- /dev/null +++ b/src/test/java/io/github/classgraph/features/NarcissusTest.java @@ -0,0 +1,28 @@ +package io.github.classgraph.features; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import nonapi.io.github.classgraph.utils.ReflectionUtils; + +/** + * NarcissusTest. + */ +class NarcissusTest { + /** Test Narcissus was able to start up OK. */ + @Test + void annotationEquality() { + ClassGraph.CIRCUMVENT_ENCAPSULATION = true; + assertThat(ReflectionUtils.getStaticFieldVal(ReflectionUtils.class, "reflectionDriver", true).getClass() + .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); + try (ScanResult scanResult = new ClassGraph().acceptPackages(NarcissusTest.class.getPackage().getName()) + .enableAllInfo().scan()) { + assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); + } finally { + ClassGraph.CIRCUMVENT_ENCAPSULATION = false; + } + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java index 8e53da1eb..2014d8fb5 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -50,6 +50,9 @@ * Issue193Test. */ public class Issue420Test { + static { + ClassGraph.CIRCUMVENT_ENCAPSULATION = true; + } /** * Test accessing a jar over Jimfs. * From 77177ebca1fec0432d11c7c7ff3e3e1e7a744f81 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 18:05:04 -0600 Subject: [PATCH 149/713] Fix unit test --- .../nonapi/io/github/classgraph/utils/ReflectionUtils.java | 5 +++++ .../java/io/github/classgraph/features/NarcissusTest.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java index 27cccd0fc..6fdb53ba8 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -52,6 +52,11 @@ public final class ReflectionUtils { private static ReflectionDriver reflectionDriver; static { + loadReflectionDriver(); + } + + /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ + public static void loadReflectionDriver() { if (ClassGraph.CIRCUMVENT_ENCAPSULATION) { try { reflectionDriver = new NarcissusReflectionDriver(); diff --git a/src/test/java/io/github/classgraph/features/NarcissusTest.java b/src/test/java/io/github/classgraph/features/NarcissusTest.java index 331d92a87..5f8637e71 100644 --- a/src/test/java/io/github/classgraph/features/NarcissusTest.java +++ b/src/test/java/io/github/classgraph/features/NarcissusTest.java @@ -16,6 +16,7 @@ class NarcissusTest { @Test void annotationEquality() { ClassGraph.CIRCUMVENT_ENCAPSULATION = true; + ReflectionUtils.loadReflectionDriver(); assertThat(ReflectionUtils.getStaticFieldVal(ReflectionUtils.class, "reflectionDriver", true).getClass() .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); try (ScanResult scanResult = new ClassGraph().acceptPackages(NarcissusTest.class.getPackage().getName()) @@ -23,6 +24,7 @@ void annotationEquality() { assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); } finally { ClassGraph.CIRCUMVENT_ENCAPSULATION = false; + ReflectionUtils.loadReflectionDriver(); } } } From 3d25ad749d958fc353df6063282ebaa02b31f500 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 18:06:29 -0600 Subject: [PATCH 150/713] [maven-release-plugin] prepare release classgraph-4.8.122 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8464f021c..6b7fbfd96 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.122-SNAPSHOT + 4.8.122 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.122 From 28631e2d3e7623b04b885b056b072500d366623c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Oct 2021 18:06:32 -0600 Subject: [PATCH 151/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6b7fbfd96..394ceddfb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.122 + 4.8.123-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.122 + classgraph-4.8.118 From 7f123b4ab2fb794561376203df314e22fbeb5d14 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Oct 2021 00:51:26 -0600 Subject: [PATCH 152/713] Upgrade to Narcissus 1.0.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 394ceddfb..43673780a 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ io.github.toolfactory narcissus - 1.0.5 + 1.0.6 true From 4d99134c89184afc8fc1525b4f6911a284503b3c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Oct 2021 01:10:22 -0600 Subject: [PATCH 153/713] Remove warning --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java index ed16916ef..5da03c36c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -43,6 +43,7 @@ public class ClassLoaderHandlerRegistry { /** * Default ClassLoaderHandlers. If a ClassLoaderHandler is added to ClassGraph, it should be added to this list. */ + @SuppressWarnings("null") public static final List CLASS_LOADER_HANDLERS = // Collections.unmodifiableList(Arrays.asList( // ClassLoaderHandlers for other ClassLoaders that are handled by ClassGraph From 207aa12e90ad9b9586fb3c1d46bd8edfb89cfc89 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Oct 2021 01:13:57 -0600 Subject: [PATCH 154/713] [maven-release-plugin] prepare release classgraph-4.8.123 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 43673780a..c945205e1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.123-SNAPSHOT + 4.8.123 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.123 From 6ea2deb82129f3f44c6b381ac6ae537d498bd07b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Oct 2021 01:13:59 -0600 Subject: [PATCH 155/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c945205e1..aa87c6f6d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.123 + 4.8.124-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.123 + classgraph-4.8.118 From c7ad4f5c09de9f3d586178a7161d568391d4a0d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Oct 2021 02:12:06 -0600 Subject: [PATCH 156/713] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c6c61fa3..bf98a0159 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That wi You can circumvent this restriction by: -* Upgrading ClassGraph to at least version 4.8.120 +* Upgrading ClassGraph to at least version 4.8.123 * Adding the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). * Setting `ClassGraph.CIRCUMVENT_ENCAPSULATION = true;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). From 80fa028d78530ead91c8248285b45b3b00aba420 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 14:20:15 -0600 Subject: [PATCH 157/713] Refactoring --- .../io/github/classgraph/AnnotationInfo.java | 6 +- .../io/github/classgraph/ModulePathInfo.java | 10 +- .../github/classgraph/ModuleReaderProxy.java | 30 +- .../java/io/github/classgraph/ModuleRef.java | 38 +- .../AntClassLoaderHandler.java | 4 +- .../CxfContainerClassLoaderHandler.java | 4 +- .../EquinoxClassLoaderHandler.java | 46 +- ...quinoxContextFinderClassLoaderHandler.java | 4 +- .../FallbackClassLoaderHandler.java | 82 +- .../FelixClassLoaderHandler.java | 20 +- .../JBossClassLoaderHandler.java | 34 +- .../JPMSClassLoaderHandler.java | 6 +- .../OSGiDefaultClassLoaderHandler.java | 10 +- ...DelegationOrderTestClassLoaderHandler.java | 6 +- ...assWorldsClassRealmClassLoaderHandler.java | 14 +- .../QuarkusClassLoaderHandler.java | 20 +- .../TomcatWebappClassLoaderBaseHandler.java | 28 +- .../UnoOneJarClassLoaderHandler.java | 4 +- .../WeblogicClassLoaderHandler.java | 6 +- .../WebsphereLibertyClassLoaderHandler.java | 26 +- ...ebsphereTraditionalClassLoaderHandler.java | 4 +- .../classgraph/classpath/ModuleFinder.java | 28 +- .../reflection/NarcissusReflectionDriver.java | 161 ++++ .../reflection/ReflectionDriver.java | 284 +++++++ .../reflection/ReflectionUtils.java | 309 +++++++ .../reflection/StandardReflectionDriver.java | 122 +++ .../classgraph/utils/ReflectionUtils.java | 772 ------------------ .../classgraph/features/NarcissusTest.java | 4 +- .../issues/issue420/Issue420Test.java | 3 +- 29 files changed, 1095 insertions(+), 990 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java create mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java create mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java create mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java delete mode 100644 src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 7dbcba342..1228b06cc 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -40,8 +40,8 @@ import java.util.Map.Entry; import java.util.Set; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Holds metadata about a specific annotation instance on a class, method, method parameter or field. */ public class AnnotationInfo extends ScanResultObject implements Comparable, HasName { @@ -354,8 +354,8 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg for (final Entry ent : annotationParameterValuesInstantiated.entrySet()) { final String paramName = ent.getKey(); final Object paramVal = ent.getValue(); - final Object otherParamVal = ReflectionUtils.invokeMethod(args[0], paramName, - /* 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 return false; diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index c79871632..341cb74a4 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -34,8 +34,8 @@ import java.util.List; import java.util.Set; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.JarUtils; -import nonapi.io.github.classgraph.utils.ReflectionUtils; import nonapi.io.github.classgraph.utils.StringUtils; /** @@ -135,12 +135,12 @@ public ModulePathInfo() { final Class managementFactory = ReflectionUtils .classForNameOrNull("java.lang.management.ManagementFactory"); final Object runtimeMXBean = managementFactory == null ? null - : ReflectionUtils.invokeStaticMethod(managementFactory, "getRuntimeMXBean", - /* throwException = */ false); + : ReflectionUtils.invokeStaticMethod(/* throwException = */ false, managementFactory, + "getRuntimeMXBean"); @SuppressWarnings("unchecked") final List commandlineArguments = runtimeMXBean == null ? null - : (List) ReflectionUtils.invokeMethod(runtimeMXBean, "getInputArguments", - /* throwException = */ false); + : (List) ReflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean, + "getInputArguments"); if (commandlineArguments != null) { for (final String arg : commandlineArguments) { for (int i = 0; i < fields.size(); i++) { diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index ba4e2379b..8b0d9103a 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -34,7 +34,7 @@ import java.nio.ByteBuffer; import java.util.List; -import nonapi.io.github.classgraph.utils.ReflectionUtils; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** A ModuleReader proxy, written using reflection to preserve backwards compatibility with JDK 7 and 8. */ public class ModuleReaderProxy implements Closeable { @@ -51,8 +51,8 @@ public class ModuleReaderProxy implements Closeable { collectorClass = ReflectionUtils.classForNameOrNull("java.util.stream.Collector"); final Class collectorsClass = ReflectionUtils.classForNameOrNull("java.util.stream.Collectors"); if (collectorsClass != null) { - collectorsToList = ReflectionUtils.invokeStaticMethod(collectorsClass, "toList", - /* throwException = */ true); + collectorsToList = ReflectionUtils.invokeStaticMethod(/* throwException = */ true, collectorsClass, + "toList"); } } @@ -66,8 +66,8 @@ public class ModuleReaderProxy implements Closeable { */ ModuleReaderProxy(final ModuleRef moduleRef) throws IOException { try { - moduleReader = (AutoCloseable) ReflectionUtils.invokeMethod(moduleRef.getReference(), "open", - /* throwException = */ true); + moduleReader = (AutoCloseable) ReflectionUtils.invokeMethod(/* throwException = */ true, + moduleRef.getReference(), "open"); if (moduleReader == null) { throw new IllegalArgumentException("moduleReference.open() should not return null"); } @@ -104,13 +104,13 @@ public List list() throws SecurityException { if (collectorsToList == null) { throw new IllegalArgumentException("Could not call Collectors.toList()"); } - final Object /* Stream */ resourcesStream = ReflectionUtils.invokeMethod(moduleReader, "list", - /* throwException = */ true); + final Object /* Stream */ resourcesStream = ReflectionUtils + .invokeMethod(/* throwException = */ true, moduleReader, "list"); if (resourcesStream == null) { throw new IllegalArgumentException("Could not call moduleReader.list()"); } - final Object resourcesList = ReflectionUtils.invokeMethod(resourcesStream, "collect", collectorClass, - collectorsToList, /* throwException = */ true); + 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())"); } @@ -133,13 +133,13 @@ public List list() throws SecurityException { */ private Object openOrRead(final String path, final boolean open) throws SecurityException { final String methodName = open ? "open" : "read"; - final Object /* Optional */ optionalInputStream = ReflectionUtils.invokeMethod(moduleReader, - methodName, String.class, path, /* throwException = */ true); + final Object /* Optional */ optionalInputStream = ReflectionUtils + .invokeMethod(/* throwException = */ true, moduleReader, methodName, String.class, path); if (optionalInputStream == null) { throw new IllegalArgumentException("Got null result from moduleReader." + methodName + "(name)"); } - final Object /* InputStream */ inputStream = ReflectionUtils.invokeMethod(optionalInputStream, "get", - /* throwException = */ true); + final Object /* InputStream */ inputStream = ReflectionUtils.invokeMethod(/* throwException = */ true, + optionalInputStream, "get"); if (inputStream == null) { throw new IllegalArgumentException("Got null result from moduleReader." + methodName + "(name).get()"); } @@ -182,7 +182,7 @@ public ByteBuffer read(final String path) throws SecurityException, OutOfMemoryE * The {@link ByteBuffer} to release. */ public void release(final ByteBuffer byteBuffer) { - ReflectionUtils.invokeMethod(moduleReader, "release", ByteBuffer.class, byteBuffer, - /* throwException = */ true); + ReflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "release", ByteBuffer.class, + byteBuffer); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 0484d3716..96a9b838b 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -35,8 +35,8 @@ import java.util.List; import java.util.Set; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.CollectionUtils; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** A ModuleReference proxy, written using reflection to preserve backwards compatibility with JDK 7 and 8. */ public class ModuleRef implements Comparable { @@ -88,46 +88,46 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { this.reference = moduleReference; this.layer = moduleLayer; - this.descriptor = ReflectionUtils.invokeMethod(moduleReference, "descriptor", /* throwException = */ true); + 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(this.descriptor, "name", /* throwException = */ true); + this.name = (String) ReflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor, "name"); @SuppressWarnings("unchecked") - final Set modulePackages = (Set) ReflectionUtils.invokeMethod(this.descriptor, "packages", - /* throwException = */ true); + final Set modulePackages = (Set) 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(this.descriptor, "rawVersion", - /* throwException = */ true); + final Object optionalRawVersion = ReflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor, + "rawVersion"); if (optionalRawVersion != null) { - final Boolean isPresent = (Boolean) ReflectionUtils.invokeMethod(optionalRawVersion, "isPresent", - /* throwException = */ true); + final Boolean isPresent = (Boolean) ReflectionUtils.invokeMethod(/* throwException = */ true, + optionalRawVersion, "isPresent"); if (isPresent != null && isPresent) { - this.rawVersion = (String) ReflectionUtils.invokeMethod(optionalRawVersion, "get", - /* throwException = */ true); + this.rawVersion = (String) ReflectionUtils.invokeMethod(/* throwException = */ true, + optionalRawVersion, "get"); } } - final Object moduleLocationOptional = ReflectionUtils.invokeMethod(moduleReference, "location", - /* 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(moduleLocationOptional, "isPresent", - /* 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(moduleLocationOptional, "get", - /* throwException = */ true); + this.location = (URI) ReflectionUtils.invokeMethod(/* throwException = */ true, moduleLocationOptional, + "get"); if (this.location == null) { // Should not happen throw new IllegalArgumentException("moduleReference.location().get() should not return null"); @@ -137,8 +137,8 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { } // Find the classloader for the module - this.classLoader = (ClassLoader) ReflectionUtils.invokeMethod(moduleLayer, "findLoader", String.class, - this.name, /* throwException = */ true); + this.classLoader = (ClassLoader) ReflectionUtils.invokeMethod(/* throwException = */ true, moduleLayer, + "findLoader", String.class, this.name); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java index 070c859e2..f20673fd9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Extract classpath entries from the Ant ClassLoader. */ class AntClassLoaderHandler implements ClassLoaderHandler { @@ -84,7 +84,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { classpathOrder.addClasspathPathStr( - (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, + (String) ReflectionUtils.invokeMethod(false, classLoader, "getClasspath"), classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java index e9c4ba372..b5664f247 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** ClassLoaderHandler that is able to extract the URLs from a CxfContainerClassLoader. */ class CxfContainerClassLoaderHandler implements ClassLoaderHandler { @@ -74,7 +74,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // Ignore } // tccl = TomcatClassLoader - classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(classLoader, "tccl", false), + classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(false, classLoader, "tccl"), /* isParent = */ false, log); // This classloader doesn't actually load any classes, but add it to the order to improve logging classLoaderOrder.add(classLoader, log); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java index 3a1159e76..aaf8b315d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -34,9 +34,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Extract classpath entries from the Eclipse Equinox ClassLoader. @@ -106,11 +106,11 @@ private static void addBundleFile(final Object bundlefile, final Set pat // Don't get stuck in infinite loop if (bundlefile != null && path.add(bundlefile)) { // type File - final Object baseFile = ReflectionUtils.getFieldVal(bundlefile, "basefile", false); + final Object baseFile = ReflectionUtils.getFieldVal(false, bundlefile, "basefile"); if (baseFile != null) { boolean foundClassPathElement = false; for (final String fieldName : FIELD_NAMES) { - final Object fieldVal = ReflectionUtils.getFieldVal(bundlefile, fieldName, false); + final Object fieldVal = ReflectionUtils.getFieldVal(false, bundlefile, fieldName); if (fieldVal != null) { foundClassPathElement = true; // We found the base file and a classpath element, e.g. "bin/" @@ -119,8 +119,8 @@ private static void addBundleFile(final Object bundlefile, final Set pat if (bundlefile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { // Handle nested ZipBundleFile with "!/" separator - final Object baseBundleFile = ReflectionUtils.getFieldVal(bundlefile, "baseBundleFile", - false); + final Object baseBundleFile = ReflectionUtils.getFieldVal(false, bundlefile, + "baseBundleFile"); if (baseBundleFile != null && baseBundleFile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { base = baseBundleFile; @@ -138,9 +138,9 @@ private static void addBundleFile(final Object bundlefile, final Set pat } } - addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "wrapped", false), path, classLoader, + addBundleFile(ReflectionUtils.getFieldVal(false, bundlefile, "wrapped"), path, classLoader, classpathOrderOut, scanSpec, log); - addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "next", false), path, classLoader, + addBundleFile(ReflectionUtils.getFieldVal(false, bundlefile, "next"), path, classLoader, classpathOrderOut, scanSpec, log); } } @@ -162,13 +162,13 @@ private static void addBundleFile(final Object bundlefile, final Set pat private static void addClasspathEntries(final Object owner, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { // type ClasspathEntry[] - final Object entries = ReflectionUtils.getFieldVal(owner, "entries", false); + final Object entries = ReflectionUtils.getFieldVal(false, owner, "entries"); if (entries != null) { for (int i = 0, n = Array.getLength(entries); i < n; i++) { // type ClasspathEntry final Object entry = Array.get(entries, i); // type BundleFile - final Object bundlefile = ReflectionUtils.getFieldVal(entry, "bundlefile", false); + final Object bundlefile = ReflectionUtils.getFieldVal(false, entry, "bundlefile"); addBundleFile(bundlefile, new HashSet<>(), classLoader, classpathOrderOut, scanSpec, log); } } @@ -189,11 +189,11 @@ private static void addClasspathEntries(final Object owner, final ClassLoader cl public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { // type ClasspathManager - final Object manager = ReflectionUtils.getFieldVal(classLoader, "manager", false); + final Object manager = ReflectionUtils.getFieldVal(false, classLoader, "manager"); addClasspathEntries(manager, classLoader, classpathOrder, scanSpec, log); // type FragmentClasspath[] - final Object fragments = ReflectionUtils.getFieldVal(manager, "fragments", false); + final Object fragments = ReflectionUtils.getFieldVal(false, manager, "fragments"); if (fragments != null) { for (int f = 0, fragLength = Array.getLength(fragments); f < fragLength; f++) { // type FragmentClasspath @@ -204,33 +204,33 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // Only read system bundles once (all bundles should give the same results for this). if (!alreadyReadSystemBundles) { // type BundleLoader - final Object delegate = ReflectionUtils.getFieldVal(classLoader, "delegate", false); + final Object delegate = ReflectionUtils.getFieldVal(false, classLoader, "delegate"); // type EquinoxContainer - final Object container = ReflectionUtils.getFieldVal(delegate, "container", false); + final Object container = ReflectionUtils.getFieldVal(false, delegate, "container"); // type Storage - final Object storage = ReflectionUtils.getFieldVal(container, "storage", false); + final Object storage = ReflectionUtils.getFieldVal(false, container, "storage"); // type ModuleContainer - final Object moduleContainer = ReflectionUtils.getFieldVal(storage, "moduleContainer", false); + final Object moduleContainer = ReflectionUtils.getFieldVal(false, storage, "moduleContainer"); // type ModuleDatabase - final Object moduleDatabase = ReflectionUtils.getFieldVal(moduleContainer, "moduleDatabase", false); + final Object moduleDatabase = ReflectionUtils.getFieldVal(false, moduleContainer, "moduleDatabase"); // type HashMap - final Object modulesById = ReflectionUtils.getFieldVal(moduleDatabase, "modulesById", false); + final Object modulesById = ReflectionUtils.getFieldVal(false, moduleDatabase, "modulesById"); // type EquinoxSystemModule (module 0 is always the system module) - final Object module0 = ReflectionUtils.invokeMethod(modulesById, "get", Object.class, 0L, false); + final Object module0 = ReflectionUtils.invokeMethod(false, modulesById, "get", Object.class, 0L); // type Bundle - final Object bundle = ReflectionUtils.invokeMethod(module0, "getBundle", false); + final Object bundle = ReflectionUtils.invokeMethod(false, module0, "getBundle"); // type BundleContext - final Object bundleContext = ReflectionUtils.invokeMethod(bundle, "getBundleContext", false); + final Object bundleContext = ReflectionUtils.invokeMethod(false, bundle, "getBundleContext"); // type Bundle[] - final Object bundles = ReflectionUtils.invokeMethod(bundleContext, "getBundles", false); + final Object bundles = ReflectionUtils.invokeMethod(false, bundleContext, "getBundles"); if (bundles != null) { for (int i = 0, n = Array.getLength(bundles); i < n; i++) { // type EquinoxBundle final Object equinoxBundle = Array.get(bundles, i); // type EquinoxModule - final Object module = ReflectionUtils.getFieldVal(equinoxBundle, "module", false); + final Object module = ReflectionUtils.getFieldVal(false, equinoxBundle, "module"); // type String - String location = (String) ReflectionUtils.getFieldVal(module, "location", false); + String location = (String) ReflectionUtils.getFieldVal(false, module, "location"); if (location != null) { final int fileIdx = location.indexOf("file:"); if (fileIdx >= 0) { diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java index 50bf9d8e0..385e9978f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Extract classpath entries from the Eclipse Equinox ContextFinder ClassLoader. */ class EquinoxContextFinderClassLoaderHandler implements ClassLoaderHandler { @@ -66,7 +66,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, final LogNode log) { classLoaderOrder.delegateTo( - (ClassLoader) ReflectionUtils.getFieldVal(classLoader, "parentContextClassLoader", false), + (ClassLoader) ReflectionUtils.getFieldVal(false, classLoader, "parentContextClassLoader"), /* isParent = */ true, log); classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); classLoaderOrder.add(classLoader, log); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java index 1b222487c..243126a9c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Fallback ClassLoaderHandler. Tries to get classpath from a range of possible method and field names. @@ -88,84 +88,84 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final ScanSpec scanSpec, final LogNode log) { boolean valid = false; valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, scanSpec, log); + ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, log); + ReflectionUtils.invokeMethod(false, classLoader, "getClasspath"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "classpath", false), classLoader, scanSpec, log); + ReflectionUtils.invokeMethod(false, classLoader, "classpath"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "classPath", false), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "cp", false), + ReflectionUtils.invokeMethod(false, classLoader, "classPath"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "cp"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.getFieldVal(classLoader, "classpath", false), classLoader, scanSpec, log); + ReflectionUtils.getFieldVal(false, classLoader, "classpath"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.getFieldVal(classLoader, "classPath", false), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "cp", false), + ReflectionUtils.getFieldVal(false, classLoader, "classPath"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "cp"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getPath", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getPath"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getPaths", false), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "path", false), + ReflectionUtils.invokeMethod(false, classLoader, "getPaths"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "path"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "paths", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "paths"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "paths"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "paths"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getDir", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getDir"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getDirs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getDirs"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dir", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "dir"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dirs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "dirs"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dir", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "dir"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dirs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "dirs"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getFile", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getFile"), classLoader, scanSpec, log); valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getFiles", false), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "file", false), + ReflectionUtils.invokeMethod(false, classLoader, "getFiles"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "file"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "files", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "files"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "file", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "file"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "files", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "files"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getJar", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getJar"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getJars", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getJars"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jar", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "jar"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jars", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "jars"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jar", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "jar"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jars", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "jars"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getURL", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getURL"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getURLs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getURLs"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getUrl", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getUrl"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getUrls", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getUrls"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "url", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "url"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "urls", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "urls"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "url", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "url"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "urls", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "urls"), classLoader, scanSpec, log); if (log != null) { log.log("FallbackClassLoaderHandler " + (valid ? "found" : "did not find") diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java index e5ff8f5fd..ff2c82ac6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -35,9 +35,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Custom Class Loader Handler for OSGi Felix ClassLoader. @@ -92,7 +92,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * @return the content location */ private static File getContentLocation(final Object content) { - return (File) ReflectionUtils.invokeMethod(content, "getFile", false); + return (File) ReflectionUtils.invokeMethod(false, content, "getFile"); } /** @@ -118,17 +118,17 @@ private static void addBundle(final Object bundleWiring, final ClassLoader class bundles.add(bundleWiring); // Get the revision for this wiring - final Object revision = ReflectionUtils.invokeMethod(bundleWiring, "getRevision", false); + final Object revision = ReflectionUtils.invokeMethod(false, bundleWiring, "getRevision"); // Get the contents - final Object content = ReflectionUtils.invokeMethod(revision, "getContent", false); + final Object content = ReflectionUtils.invokeMethod(false, revision, "getContent"); final File location = content != null ? getContentLocation(content) : null; if (location != null) { // Add the bundle object classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); // And any embedded content - final List embeddedContent = (List) ReflectionUtils.invokeMethod(revision, "getContentPath", - false); + final List embeddedContent = (List) ReflectionUtils.invokeMethod(false, revision, + "getContentPath"); if (embeddedContent != null) { for (final Object embedded : embeddedContent) { if (embedded != content) { @@ -158,17 +158,17 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final ScanSpec scanSpec, final LogNode log) { // Get the wiring for the ClassLoader's bundle final Set bundles = new HashSet<>(); - final Object bundleWiring = ReflectionUtils.getFieldVal(classLoader, "m_wiring", false); + final Object bundleWiring = ReflectionUtils.getFieldVal(false, classLoader, "m_wiring"); addBundle(bundleWiring, classLoader, classpathOrder, bundles, scanSpec, log); // Deal with any other bundles we might be wired to. TODO: Use the ScanSpec to narrow down the list of wires // that we follow. - final List requiredWires = (List) ReflectionUtils.invokeMethod(bundleWiring, "getRequiredWires", - String.class, null, false); + final List requiredWires = (List) ReflectionUtils.invokeMethod(false, bundleWiring, + "getRequiredWires", String.class, null); if (requiredWires != null) { for (final Object wire : requiredWires) { - final Object provider = ReflectionUtils.invokeMethod(wire, "getProviderWiring", false); + final Object provider = ReflectionUtils.invokeMethod(false, wire, "getProviderWiring"); if (!bundles.contains(provider)) { addBundle(provider, classLoader, classpathOrder, bundles, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 8971ea014..f25a50f9b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -39,10 +39,10 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Extract classpath entries from the JBoss ClassLoader. See: @@ -104,12 +104,12 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas return; } // PathResourceLoader has root field, which is a Path object - final Object root = ReflectionUtils.getFieldVal(resourceLoader, "root", false); + final Object root = ReflectionUtils.getFieldVal(false, resourceLoader, "root"); // type VirtualFile - final File physicalFile = (File) ReflectionUtils.invokeMethod(root, "getPhysicalFile", false); + final File physicalFile = (File) ReflectionUtils.invokeMethod(false, root, "getPhysicalFile"); String path = null; if (physicalFile != null) { - final String name = (String) ReflectionUtils.invokeMethod(root, "getName", false); + final String name = (String) ReflectionUtils.invokeMethod(false, root, "getName"); if (name != null) { // getParentFile() removes "contents" directory final File file = new File(physicalFile.getParentFile(), name); @@ -123,7 +123,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas path = physicalFile.getAbsolutePath(); } } else { - path = (String) ReflectionUtils.invokeMethod(root, "getPathName", false); + path = (String) ReflectionUtils.invokeMethod(false, root, "getPathName"); if (path == null) { // Try Path or File final File file = root instanceof Path ? ((Path) root).toFile() @@ -134,7 +134,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas } } if (path == null) { - final File file = (File) ReflectionUtils.getFieldVal(resourceLoader, "fileOfJar", false); + final File file = (File) ReflectionUtils.getFieldVal(false, resourceLoader, "fileOfJar"); if (file != null) { path = file.getAbsolutePath(); } @@ -171,12 +171,12 @@ private static void handleRealModule(final Object module, final Set visi // Avoid extracting paths from the same module more than once return; } - ClassLoader moduleLoader = (ClassLoader) ReflectionUtils.invokeMethod(module, "getClassLoader", false); + ClassLoader moduleLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, module, "getClassLoader"); if (moduleLoader == null) { moduleLoader = classLoader; } // type VFSResourceLoader[] - final Object vfsResourceLoaders = ReflectionUtils.invokeMethod(moduleLoader, "getResourceLoaders", false); + final Object vfsResourceLoaders = ReflectionUtils.invokeMethod(false, moduleLoader, "getResourceLoaders"); if (vfsResourceLoaders != null) { for (int i = 0, n = Array.getLength(vfsResourceLoaders); i < n; i++) { // type JarFileResourceLoader for jars, VFSResourceLoader for exploded jars, PathResourceLoader @@ -206,29 +206,29 @@ private static void handleRealModule(final Object module, final Set visi */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Object module = ReflectionUtils.invokeMethod(classLoader, "getModule", false); - final Object callerModuleLoader = ReflectionUtils.invokeMethod(module, "getCallerModuleLoader", false); + final Object module = ReflectionUtils.invokeMethod(false, classLoader, "getModule"); + final Object callerModuleLoader = ReflectionUtils.invokeMethod(false, module, "getCallerModuleLoader"); final Set visitedModules = new HashSet<>(); @SuppressWarnings("unchecked") - final Map moduleMap = (Map) ReflectionUtils.getFieldVal(callerModuleLoader, - "moduleMap", false); + final Map moduleMap = (Map) ReflectionUtils.getFieldVal(false, + callerModuleLoader, "moduleMap"); for (final Entry ent : moduleMap.entrySet()) { // type FutureModule final Object val = ent.getValue(); // type Module - final Object realModule = ReflectionUtils.invokeMethod(val, "getModule", false); + final Object realModule = ReflectionUtils.invokeMethod(false, val, "getModule"); handleRealModule(realModule, visitedModules, classLoader, classpathOrder, scanSpec, log); } // type Map> @SuppressWarnings("unchecked") - final Map> pathsMap = (Map>) ReflectionUtils.invokeMethod(module, - "getPaths", false); + final Map> pathsMap = (Map>) ReflectionUtils.invokeMethod(false, module, + "getPaths"); for (final Entry> ent : pathsMap.entrySet()) { for (final Object /* ModuleClassLoader$1 */ localLoader : ent.getValue()) { // type ModuleClassLoader (outer class) - final Object moduleClassLoader = ReflectionUtils.getFieldVal(localLoader, "this$0", false); + final Object moduleClassLoader = ReflectionUtils.getFieldVal(false, localLoader, "this$0"); // type Module - final Object realModule = ReflectionUtils.getFieldVal(moduleClassLoader, "module", false); + final Object realModule = ReflectionUtils.getFieldVal(false, moduleClassLoader, "module"); handleRealModule(realModule, visitedModules, classLoader, classpathOrder, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java index 0d1dadf8d..4086c06b8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -32,9 +32,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * A placeloader ClassLoaderHandler that matches Java 9+ classloaders, but does not attempt to extract URLs from @@ -94,9 +94,9 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // However, it is possible for a Java agent to extend UCP by adding directly to the `ucp` field // (#537), and there is no way to read this field. Therefore, we need to use Narcissus to break // Java's encapsulation to read this, for this small corner case. - final Object ucpVal = ReflectionUtils.getFieldVal(classLoader, "ucp", false); + final Object ucpVal = ReflectionUtils.getFieldVal(false, classLoader, "ucp"); if (ucpVal != null) { - final URL[] urls = (URL[]) ReflectionUtils.invokeMethod(ucpVal, "getURLs", false); + final URL[] urls = (URL[]) ReflectionUtils.invokeMethod(false, ucpVal, "getURLs"); classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java index dc6a74f54..fb844d620 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -32,9 +32,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Handle the OSGi DefaultClassLoader. @@ -89,12 +89,12 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Object classpathManager = ReflectionUtils.invokeMethod(classLoader, "getClasspathManager", false); - final Object[] entries = (Object[]) ReflectionUtils.getFieldVal(classpathManager, "entries", false); + final Object classpathManager = ReflectionUtils.invokeMethod(false, classLoader, "getClasspathManager"); + final Object[] entries = (Object[]) ReflectionUtils.getFieldVal(false, classpathManager, "entries"); if (entries != null) { for (final Object entry : entries) { - final Object bundleFile = ReflectionUtils.invokeMethod(entry, "getBundleFile", false); - final File baseFile = (File) ReflectionUtils.invokeMethod(bundleFile, "getBaseFile", false); + final Object bundleFile = ReflectionUtils.invokeMethod(false, entry, "getBundleFile"); + final File baseFile = (File) ReflectionUtils.invokeMethod(false, bundleFile, "getBaseFile"); if (baseFile != null) { classpathOrder.addClasspathEntry(baseFile.getPath(), classLoader, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java index 9d26a0fcc..c003c93a1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** ClassLoaderHandler that is used to test PARENT_LAST delegation order. */ class ParentLastDelegationOrderTestClassLoaderHandler implements ClassLoaderHandler { @@ -84,8 +84,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final String classpath = (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", - /* throwException = */ true); + final String classpath = (String) ReflectionUtils.invokeMethod(/* throwException = */ true, classLoader, + "getClasspath"); classpathOrder.addClasspathEntry(classpath, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java index 4714f16c2..b40eb9e89 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -32,9 +32,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Handle the Plexus ClassWorlds ClassRealm ClassLoader. @@ -67,7 +67,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * @return true if classloader uses a parent-first strategy */ private static boolean isParentFirstStrategy(final ClassLoader classRealmInstance) { - final Object strategy = ReflectionUtils.getFieldVal(classRealmInstance, "strategy", false); + final Object strategy = ReflectionUtils.getFieldVal(false, classRealmInstance, "strategy"); if (strategy != null) { final String strategyClassName = strategy.getClass().getName(); if (strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy") @@ -93,13 +93,13 @@ private static boolean isParentFirstStrategy(final ClassLoader classRealmInstanc public static void findClassLoaderOrder(final ClassLoader classRealm, final ClassLoaderOrder classLoaderOrder, final LogNode log) { // From ClassRealm#loadClassFromImport(String) -> getImportClassLoader(String) - final Object foreignImports = ReflectionUtils.getFieldVal(classRealm, "foreignImports", false); + final Object foreignImports = ReflectionUtils.getFieldVal(false, classRealm, "foreignImports"); if (foreignImports != null) { @SuppressWarnings("unchecked") final SortedSet foreignImportEntries = (SortedSet) foreignImports; for (final Object entry : foreignImportEntries) { - final ClassLoader foreignImportClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(entry, - "getClassLoader", false); + final ClassLoader foreignImportClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, + entry, "getClassLoader"); // Treat foreign import classloader as if it is a parent classloader classLoaderOrder.delegateTo(foreignImportClassLoader, /* isParent = */ true, log); } @@ -117,8 +117,8 @@ public static void findClassLoaderOrder(final ClassLoader classRealm, final Clas // From ClassRealm#loadClassFromParent -- N.B. we are ignoring parentImports, which is used to filter // a class name before deciding whether or not to call the parent classloader (so ClassGraph will be // able to load classes by name that are not imported from the parent classloader). - final ClassLoader parentClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(classRealm, - "getParentClassLoader", false); + final ClassLoader parentClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, classRealm, + "getParentClassLoader"); classLoaderOrder.delegateTo(parentClassLoader, /* isParent = */ true, log); classLoaderOrder.delegateTo(classRealm.getParent(), /* isParent = */ true, log); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index 13317a2ff..faa9ced09 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -34,9 +34,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Extract classpath entries from the Quarkus ClassLoader. @@ -116,14 +116,14 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class @SuppressWarnings("unchecked") private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - for (final Object element : (Collection) ReflectionUtils.getFieldVal(classLoader, "elements", - false)) { + for (final Object element : (Collection) ReflectionUtils.getFieldVal(false, classLoader, + "elements")) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "file", false), classLoader, + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "file"), classLoader, scanSpec, log); } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "root", false), classLoader, + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "root"), classLoader, scanSpec, log); } } @@ -132,8 +132,8 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl @SuppressWarnings("unchecked") private static void findClasspathOrderForRuntimeClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Collection applicationClassDirectories = (Collection) ReflectionUtils - .getFieldVal(classLoader, "applicationClassDirectories", false); + final Collection applicationClassDirectories = (Collection) ReflectionUtils.getFieldVal(false, + classLoader, "applicationClassDirectories"); if (applicationClassDirectories != null) { for (final Path path : applicationClassDirectories) { classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log); @@ -144,12 +144,12 @@ private static void findClasspathOrderForRuntimeClassloader(final ClassLoader cl @SuppressWarnings("unchecked") private static void findClasspathOrderForRunnerClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - for (final Object[] elementArray : ((Map) ReflectionUtils.getFieldVal(classLoader, - "resourceDirectoryMap", false)).values()) { + for (final Object[] elementArray : ((Map) ReflectionUtils.getFieldVal(false, classLoader, + "resourceDirectoryMap")).values()) { for (final Object element : elementArray) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.runner.JarResource".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "jarPath", false), + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "jarPath"), classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index a8fcd8af0..a76ee7511 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -33,9 +33,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Extract classpath entries from the Tomcat/Catalina WebappClassLoaderBase. */ class TomcatWebappClassLoaderBaseHandler implements ClassLoaderHandler { @@ -64,7 +64,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * @return true if this classloader delegates to its parent. */ private static boolean isParentFirst(final ClassLoader classLoader) { - final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); + final Object delegateObject = ReflectionUtils.getFieldVal(false, classLoader, "delegate"); if (delegateObject != null) { return (boolean) delegateObject; } @@ -122,16 +122,16 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { // type StandardRoot (implements WebResourceRoot) - final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); + final Object resources = ReflectionUtils.invokeMethod(false, classLoader, "getResources"); // type List - final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); + final Object baseURLs = ReflectionUtils.invokeMethod(false, resources, "getBaseUrls"); classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); // type List> // members: preResources, mainResources, classResources, jarResources, // postResources @SuppressWarnings("unchecked") - final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", - false); + final List> allResources = (List>) ReflectionUtils.getFieldVal(false, resources, + "allResources"); if (allResources != null) { // type List for (final List webResourceSetList : allResources) { @@ -141,23 +141,23 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class for (final Object webResourceSet : webResourceSetList) { if (webResourceSet != null) { // For DirResourceSet - final File file = (File) ReflectionUtils.invokeMethod(webResourceSet, "getFileBase", false); + final File file = (File) ReflectionUtils.invokeMethod(false, webResourceSet, "getFileBase"); String base = file == null ? null : file.getPath(); if (base == null) { // For FileResourceSet - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBase", false); + base = (String) ReflectionUtils.invokeMethod(false, webResourceSet, "getBase"); } if (base == null) { // For JarResourceSet and JarWarResourceSet // The absolute path to the WAR file on the file system in which the JAR is // located - base = (String) ReflectionUtils.invokeMethod(webResourceSet, "getBaseUrlString", false); + base = (String) ReflectionUtils.invokeMethod(false, webResourceSet, "getBaseUrlString"); } if (base != null) { // For JarWarResourceSet: the path within the WAR file where the JAR file is // located - final String archivePath = (String) ReflectionUtils.getFieldVal(webResourceSet, - "archivePath", false); + final String archivePath = (String) ReflectionUtils.getFieldVal(false, webResourceSet, + "archivePath"); if (archivePath != null && !archivePath.isEmpty()) { // If archivePath is non-null, this is a jar within a war base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); @@ -168,8 +168,8 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); // The path within this WebResourceSet where resources will be served from, // e.g. for a resource JAR, this would be "META-INF/resources" - final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, - "getInternalPath", false); + final String internalPath = (String) ReflectionUtils.invokeMethod(false, webResourceSet, + "getInternalPath"); if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { classpathOrder.addClasspathEntryObject(base + (isJar ? "!" : "") + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), @@ -183,7 +183,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } } // This may or may not duplicate the above - final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); + final Object urls = ReflectionUtils.invokeMethod(false, classLoader, "getURLs"); classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java index 17fc15f60..cc3a7a29f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Extract classpath entries from the Uno-Jar's JarClassLoader and One-Jar's JarClassLoader. */ class UnoOneJarClassLoaderHandler implements ClassLoaderHandler { @@ -87,7 +87,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // For Uno-Jar: - final String unoJarOneJarPath = (String) ReflectionUtils.invokeMethod(classLoader, "getOneJarPath", false); + final String unoJarOneJarPath = (String) ReflectionUtils.invokeMethod(false, classLoader, "getOneJarPath"); classpathOrder.addClasspathEntry(unoJarOneJarPath, classLoader, scanSpec, log); // If this property is defined, Uno-Jar jar path was specified on commandline. Otherwise, jar path diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java index 50cffe710..2443c04ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** Extract classpath entries from the Weblogic ClassLoaders. */ class WeblogicClassLoaderHandler implements ClassLoaderHandler { @@ -90,10 +90,10 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { classpathOrder.addClasspathPathStr( // - (String) ReflectionUtils.invokeMethod(classLoader, "getFinderClassPath", false), classLoader, + (String) ReflectionUtils.invokeMethod(false, classLoader, "getFinderClassPath"), classLoader, scanSpec, log); classpathOrder.addClasspathPathStr( // - (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, scanSpec, + (String) ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"), classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index 2d804bd52..e910c5318 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -39,9 +39,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * WebsphereLibertyClassLoaderHandler. @@ -121,7 +121,7 @@ private static Collection getPaths(final Object containerClassLoader) { } // "getContainerURLs" didn't work, try getting the container object... - final Object container = ReflectionUtils.getFieldVal(containerClassLoader, "container", false); + final Object container = ReflectionUtils.getFieldVal(false, containerClassLoader, "container"); if (container == null) { return Collections.emptyList(); } @@ -134,23 +134,23 @@ private static Collection getPaths(final Object containerClassLoader) { } // "getURLs" did not work, reverting to previous logic of introspection of the "delegate". - final Object delegate = ReflectionUtils.getFieldVal(container, "delegate", false); + final Object delegate = ReflectionUtils.getFieldVal(false, container, "delegate"); if (delegate == null) { return Collections.emptyList(); } - final String path = (String) ReflectionUtils.getFieldVal(delegate, "path", false); + final String path = (String) ReflectionUtils.getFieldVal(false, delegate, "path"); if (path != null && path.length() > 0) { return Collections.singletonList((Object) path); } - final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); + final Object base = ReflectionUtils.getFieldVal(false, delegate, "base"); if (base == null) { // giving up. return Collections.emptyList(); } - final Object archiveFile = ReflectionUtils.getFieldVal(base, "archiveFile", false); + final Object archiveFile = ReflectionUtils.getFieldVal(false, base, "archiveFile"); if (archiveFile != null) { final File file = (File) archiveFile; return Collections.singletonList((Object) file.getAbsolutePath()); @@ -173,8 +173,8 @@ private static Collection getPaths(final Object containerClassLoader) { private static Collection callGetUrls(final Object container, final String methodName) { if (container != null) { try { - final Collection results = (Collection) ReflectionUtils.invokeMethod(container, - methodName, false); + final Collection results = (Collection) ReflectionUtils.invokeMethod(false, + container, methodName); if (results != null && !results.isEmpty()) { final Collection allUrls = new HashSet<>(); for (final Object result : results) { @@ -213,11 +213,11 @@ private static Collection callGetUrls(final Object container, final Stri public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { Object smartClassPath; - final Object appLoader = ReflectionUtils.getFieldVal(classLoader, "appLoader", false); + final Object appLoader = ReflectionUtils.getFieldVal(false, classLoader, "appLoader"); if (appLoader != null) { - smartClassPath = ReflectionUtils.getFieldVal(appLoader, "smartClassPath", false); + smartClassPath = ReflectionUtils.getFieldVal(false, appLoader, "smartClassPath"); } else { - smartClassPath = ReflectionUtils.getFieldVal(classLoader, "smartClassPath", false); + smartClassPath = ReflectionUtils.getFieldVal(false, classLoader, "smartClassPath"); } if (smartClassPath != null) { // "com.ibm.ws.classloading.internal.ContainerClassLoader$SmartClassPath" @@ -230,8 +230,8 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } else { // "getClassPath" didn't work... reverting to looping over "classPath" elements. @SuppressWarnings("unchecked") - final List classPathElements = (List) ReflectionUtils.getFieldVal(smartClassPath, - "classPath", false); + final List classPathElements = (List) ReflectionUtils.getFieldVal(false, + smartClassPath, "classPath"); if (classPathElements != null && !classPathElements.isEmpty()) { for (final Object classPathElement : classPathElements) { final Collection subPaths = getPaths(classPathElement); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java index 79d18093a..7cab902eb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -30,9 +30,9 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Handle the WebSphere traditonal ClassLoaders. @@ -89,7 +89,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final String classpath = (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false); + final String classpath = (String) ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"); classpathOrder.addClasspathPathStr(classpath, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index 6013e8b53..6a98b3645 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -38,10 +38,10 @@ import java.util.Set; import io.github.classgraph.ModuleRef; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.ReflectionUtils; /** A class to find the visible modules. */ public class ModuleFinder { @@ -110,8 +110,8 @@ private static void findLayerOrder(final Object /* ModuleLayer */ layer, final Deque /* Deque */ layerOrderOut) { if (layerVisited.add(layer)) { @SuppressWarnings("unchecked") - final List /* List */ parents = (List) ReflectionUtils.invokeMethod(layer, - "parents", /* throwException = */ true); + final List /* List */ parents = (List) ReflectionUtils + .invokeMethod(/* throwException = */ true, layer, "parents"); if (parents != null) { parentLayers.addAll(parents); for (final Object parent : parents) { @@ -172,18 +172,18 @@ private static List findModuleRefs(final LinkedHashSet layers final Set /* Set */ addedModules = new HashSet<>(); final LinkedHashSet moduleRefOrder = new LinkedHashSet<>(); for (final Object /* ModuleLayer */ layer : layerOrderFinal) { - final Object /* Configuration */ configuration = ReflectionUtils.invokeMethod(layer, "configuration", - /* throwException = */ true); + final Object /* Configuration */ configuration = ReflectionUtils + .invokeMethod(/* throwException = */ true, layer, "configuration"); if (configuration != null) { // Get ModuleReferences from layer configuration @SuppressWarnings("unchecked") final Set /* Set */ modules = (Set) ReflectionUtils - .invokeMethod(configuration, "modules", /* throwException = */ true); + .invokeMethod(/* throwException = */ true, configuration, "modules"); if (modules != null) { final List modulesInLayer = new ArrayList<>(); for (final Object /* ResolvedModule */ module : modules) { - final Object /* ModuleReference */ moduleReference = ReflectionUtils.invokeMethod(module, - "reference", /* throwException = */ true); + final Object /* ModuleReference */ moduleReference = ReflectionUtils + .invokeMethod(/* throwException = */ true, module, "reference"); if (moduleReference != null && addedModules.add(moduleReference)) { try { modulesInLayer.add(new ModuleRef(moduleReference, layer)); @@ -219,11 +219,11 @@ private List findModuleRefsFromCallstack(final Class[] callStack, final LinkedHashSet layers = new LinkedHashSet<>(); if (callStack != null) { for (final Class stackFrameClass : callStack) { - final Object /* Module */ module = ReflectionUtils.invokeMethod(stackFrameClass, "getModule", - /* throwException = */ false); + final Object /* Module */ module = ReflectionUtils.invokeMethod(/* throwException = */ false, + stackFrameClass, "getModule"); if (module != null) { - final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(module, "getLayer", - /* throwException = */ true); + final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(/* throwException = */ true, + module, "getLayer"); if (layer != null) { layers.add(layer); } else { @@ -242,8 +242,8 @@ private List findModuleRefsFromCallstack(final Class[] callStack, // Ignored } if (moduleLayerClass != null) { - final Object /* ModuleLayer */ bootLayer = ReflectionUtils.invokeStaticMethod(moduleLayerClass, "boot", - /* throwException = */ false); + final Object /* ModuleLayer */ bootLayer = ReflectionUtils + .invokeStaticMethod(/* throwException = */ false, moduleLayerClass, "boot"); if (bootLayer != null) { layers.add(bootLayer); } else { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java new file mode 100644 index 000000000..a4a7ca7b7 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -0,0 +1,161 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.reflection; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Narcissus reflection driver (uses the Narcissus library, + * if it is available, which allows access to non-public fields and methods, circumventing encapsulation and + * visibility controls via JNI). + */ +class NarcissusReflectionDriver extends ReflectionDriver { + private final Map> methodNameToMethods = new HashMap<>(); + private final Class narcissusClass; + private final Method getDeclaredMethods; + private final Method findClass; + private final Method getDeclaredConstructors; + private final Method getDeclaredFields; + private final Method getField; + private final Method setField; + private final Method getStaticField; + private final Method setStaticField; + private final Method invokeMethod; + private final Method invokeStaticMethod; + + private Method findIndexedMethod(final String methodName, final Class... paramTypes) + throws NoSuchMethodException { + final List methods = methodNameToMethods.get(methodName); + if (methods != null) { + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + return method; + } + } + } + throw new NoSuchMethodException(methodName); + } + + private void indexMethods(final List methods) { + // Index Narcissus methods by name + for (final Method method : methods) { + List methodsForName = methodNameToMethods.get(method.getName()); + if (methodsForName == null) { + methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); + } + methodsForName.add(method); + } + } + + NarcissusReflectionDriver() throws Exception { + // Load Narcissus class via reflection, so that there is no runtime dependency + final StandardReflectionDriver drv = new StandardReflectionDriver(); + narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); + if (!(Boolean) drv.getStaticField(drv.findField(narcissusClass, "libraryLoaded"))) { + throw new IllegalArgumentException("Could not load Narcissus native library"); + } + + // Look up needed methods + indexMethods(drv.enumerateMethods(narcissusClass)); + findClass = findIndexedMethod("findClass", String.class); + getDeclaredMethods = findIndexedMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findIndexedMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findIndexedMethod("getDeclaredFields", Class.class); + getField = findIndexedMethod("getField", Object.class, Field.class); + setField = findIndexedMethod("setField", Object.class, Field.class, Object.class); + getStaticField = findIndexedMethod("getStaticField", Field.class); + setStaticField = findIndexedMethod("setStaticField", Field.class, Object.class); + invokeMethod = findIndexedMethod("invokeMethod", Object.class, Method.class, Object[].class); + invokeStaticMethod = findIndexedMethod("invokeStaticMethod", Method.class, Object[].class); + } + + @Override + boolean makeAccessible(final AccessibleObject accessibleObject) { + return true; + } + + @Override + Class findClass(final String className) throws Exception { + return (Class) findClass.invoke(null, className); + } + + @Override + Method[] getDeclaredMethods(final Class cls) throws Exception { + return (Method[]) getDeclaredMethods.invoke(null, cls); + } + + @SuppressWarnings("unchecked") + @Override + Constructor[] getDeclaredConstructors(final Class cls) throws Exception { + return (Constructor[]) getDeclaredConstructors.invoke(null, cls); + } + + @Override + Field[] getDeclaredFields(final Class cls) throws Exception { + return (Field[]) getDeclaredFields.invoke(null, cls); + } + + @Override + Object getField(final Object object, final Field field) throws Exception { + return getField.invoke(null, object, field); + } + + @Override + void setField(final Object object, final Field field, final Object value) throws Exception { + setField.invoke(null, object, field, value); + } + + @Override + Object getStaticField(final Field field) throws Exception { + return getStaticField.invoke(null, field); + } + + @Override + void setStaticField(final Field field, final Object value) throws Exception { + setStaticField.invoke(null, field, value); + } + + @Override + Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { + return invokeMethod.invoke(null, object, method, args); + } + + @Override + Object invokeStaticMethod(final Method method, final Object... args) throws Exception { + return invokeStaticMethod.invoke(null, method, args); + } +} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java new file mode 100644 index 000000000..9c9d69f6a --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -0,0 +1,284 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.reflection; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +/** Reflection driver */ +abstract class ReflectionDriver { + /** + * Find a class by name. + * + * @param className + * the class name + * @return the class reference + */ + abstract Class findClass(final String className) throws Exception; + + /** + * Get declared methods for class. + * + * @param cls + * the class + * @return the declared methods + */ + abstract Method[] getDeclaredMethods(Class cls) throws Exception; + + /** + * Get declared constructors for class. + * + * @param + * the generic type + * @param cls + * the class + * @return the declared constructors + */ + abstract Constructor[] getDeclaredConstructors(Class cls) throws Exception; + + /** + * Get declared fields for class. + * + * @param cls + * the class + * @return the declared fields + */ + abstract Field[] getDeclaredFields(Class cls) throws Exception; + + /** + * Get the value of a non-static field, boxing the value if necessary. + * + * @param object + * the object instance to get the field value from + * @param field + * the non-static field + * @return the value of the field + */ + abstract Object getField(final Object object, final Field field) throws Exception; + + /** + * Set the value of a non-static field, unboxing the value if necessary. + * + * @param object + * the object instance to get the field value from + * @param field + * the non-static field + * @param value + * the value to set + */ + abstract void setField(final Object object, final Field field, Object value) throws Exception; + + /** + * Get the value of a static field, boxing the value if necessary. + * + * @param field + * the static field + * @return the static field + */ + abstract Object getStaticField(final Field field) throws Exception; + + /** + * Set the value of a static field, unboxing the value if necessary. + * + * @param field + * the static field + * @param the + * value to set + */ + abstract void setStaticField(final Field field, Object value) throws Exception; + + /** + * Invoke a non-static method, boxing the result if necessary. + * + * @param object + * the object instance to invoke the method on + * @param method + * the non-static method + * @param args + * the method arguments (or {@code new Object[0]} if there are no args) + * @return the return value (possibly a boxed value) + */ + abstract Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception; + + /** + * Invoke a static method, boxing the result if necessary. + * + * @param method + * the static method + * @param args + * the method arguments (or {@code new Object[0]} if there are no args) + * @return the return value (possibly a boxed value) + */ + abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; + + /** + * Make a field or method accessible. + * + * @param accessibleObject + * the field or method. + * @return true if successful. + */ + abstract boolean makeAccessible(final AccessibleObject accessibleObject); + + /** Iterator applied to each method of a class and its superclasses/interfaces. */ + private static interface MethodIterator { + /** @return true to stop iterating, or false to continue iterating */ + boolean foundMethod(Method m) throws Exception; + } + + /** + * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to + * collect all methods of the class and its superclasses, and any default methods defined in interfaces. + * + * @param cls + * the class + */ + void forAllMethods(final Class cls, final ReflectionDriver.MethodIterator methodIter) throws Exception { + // Iterate from class to its superclasses, and find initial interfaces to start traversing from + final Set> visited = new HashSet<>(); + final LinkedList> interfaceQueue = new LinkedList<>(); + for (Class c = cls; c != null; c = c.getSuperclass()) { + for (final Method m : getDeclaredMethods(c)) { + if (methodIter.foundMethod(m)) { + return; + } + } + // Find interfaces and superinterfaces implemented by this class or its superclasses + if (c.isInterface() && visited.add(c)) { + interfaceQueue.add(c); + } + for (final Class iface : c.getInterfaces()) { + if (visited.add(iface)) { + interfaceQueue.add(iface); + } + } + } + // Traverse through interfaces looking for default methods + while (!interfaceQueue.isEmpty()) { + final Class iface = interfaceQueue.remove(); + for (final Method m : getDeclaredMethods(iface)) { + if (methodIter.foundMethod(m)) { + return; + } + } + for (final Class superIface : iface.getInterfaces()) { + if (visited.add(superIface)) { + interfaceQueue.add(superIface); + } + } + } + } + + /** + * Find a method by name and parameter types in the given class. + * + * @param cls + * the class + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name and parameter types + */ + Method findMethod(final Class cls, final String methodName, final Class... paramTypes) throws Exception { + final AtomicReference method = new AtomicReference<>(); + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes()) + // If method is not accessible, fall through and try superclass method of same + // name and paramTypes + && makeAccessible(m)) { + method.set(m); + return true; + } + return false; + } + }); + final Method m = method.get(); + if (m != null) { + return m; + } else { + throw new NoSuchMethodException(methodName); + } + } + + /** + * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also iterates up + * through superclasses, to collect all methods of the class and its superclasses. + * + * @param cls + * the class + * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. + */ + List enumerateMethods(final Class cls) throws Exception { + final List methodOrder = new ArrayList<>(); + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + methodOrder.add(m); + return false; + } + }); + return methodOrder; + } + + /** + * Find a field by name in the given class. + * + * @param cls + * the class + * @param fieldName + * the field name. + * @return the {@link Field} + * @throws NoSuchFieldException + * if the class does not contain a field of the given name + */ + Field findField(final Class cls, final String fieldName) throws Exception { + for (Class c = cls; c != null; c = c.getSuperclass()) { + for (final Field field : getDeclaredFields(c)) { + if (field.getName().equals(fieldName)) { + return field; + } + } + } + throw new NoSuchFieldException(fieldName); + } +} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java new file mode 100644 index 000000000..e7c5134bf --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -0,0 +1,309 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.reflection; + +import io.github.classgraph.ClassGraph; + +/** Reflection utility methods that can be used by ClassLoaderHandlers. */ +public final class ReflectionUtils { + /** The reflection driver to use. */ + private static ReflectionDriver reflectionDriver; + + static { + loadReflectionDriver(); + } + + /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ + public static void loadReflectionDriver() { + if (ClassGraph.CIRCUMVENT_ENCAPSULATION) { + try { + reflectionDriver = new NarcissusReflectionDriver(); + } catch (final Throwable t) { + System.err.println("Could not load Narcissus reflection driver: " + t); + // Fall back to standard reflection driver + } + } + if (reflectionDriver == null) { + reflectionDriver = new StandardReflectionDriver(); + } + } + + /** + * Constructor. + */ + private ReflectionUtils() { + // Cannot be constructed + } + + /** + * Get the value of the named field in the class of the given object or any of its superclasses. If an exception + * is thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown + * wrapping the cause, otherwise this will return null. If passed a null object, returns null unless + * throwException is true, then throws IllegalArgumentException. + * + * @param throwException + * If true, throw an exception if the field value could not be read. + * @param obj + * The object. + * @param fieldName + * The field name. + * + * @return The field value. + * @throws IllegalArgumentException + * If the field value could not be read. + */ + public static Object getFieldVal(final boolean throwException, final Object obj, final String fieldName) + throws IllegalArgumentException { + if (obj == null || fieldName == null) { + if (throwException) { + throw new NullPointerException(); + } else { + return null; + } + } + try { + return reflectionDriver.getField(obj, reflectionDriver.findField(obj.getClass(), fieldName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Can't read field " + obj.getClass().getName() + "." + fieldName + ": " + e); + } + } + return null; + } + + /** + * Get the value of the named static field in the given class or any of its superclasses. If an exception is + * thrown while trying to read the field value, and throwException is true, then IllegalArgumentException is + * thrown wrapping the cause, otherwise this will return null. If passed a null class reference, returns null + * unless throwException is true, then throws IllegalArgumentException. + * + * @param throwException + * If true, throw an exception if the field value could not be read. + * @param cls + * The class. + * @param fieldName + * The field name. + * + * @return The field value. + * @throws IllegalArgumentException + * If the field value could not be read. + */ + public static Object getStaticFieldVal(final boolean throwException, final Class cls, final String fieldName) + throws IllegalArgumentException { + if (cls == null || fieldName == null) { + if (throwException) { + throw new NullPointerException(); + } else { + return null; + } + } + try { + return reflectionDriver.getStaticField(reflectionDriver.findField(cls, fieldName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Can't read static field " + cls.getName() + "." + fieldName + ": " + e); + } + } + return null; + } + + /** + * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to + * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, + * otherwise this will return null. If passed a null object, returns null unless throwException is true, then + * throws IllegalArgumentException. + * + * @param throwException + * If true, throw an exception if the field value could not be read. + * @param obj + * The object. + * @param methodName + * The method name. + * + * @return The result of the method invocation. + * @throws IllegalArgumentException + * If the method could not be invoked. + */ + public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName) + throws IllegalArgumentException { + if (obj == null || methodName == null) { + if (throwException) { + throw new IllegalArgumentException("Unexpected null argument"); + } else { + return null; + } + } + try { + return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + return null; + } + } + + /** + * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to + * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, + * otherwise this will return null. If passed a null object, returns null unless throwException is true, then + * throws IllegalArgumentException. + * + * @param throwException + * Whether to throw an exception on failure. + * @param obj + * The object. + * @param methodName + * The method name. + * @param argType + * The type of the method argument. + * @param param + * The parameter value to use when invoking the method. + * + * @return The result of the method invocation. + * @throws IllegalArgumentException + * If the method could not be invoked. + */ + public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName, + final Class argType, final Object param) throws IllegalArgumentException { + if (obj == null || methodName == null || argType == null) { + if (throwException) { + throw new IllegalArgumentException("Unexpected null argument"); + } else { + return null; + } + } + try { + return reflectionDriver.invokeMethod(obj, + reflectionDriver.findMethod(obj.getClass(), methodName, argType), param); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + } + return null; + } + } + + /** + * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException + * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If + * passed a null class reference, returns null unless throwException is true, then throws + * IllegalArgumentException. + * + * @param throwException + * Whether to throw an exception on failure. + * @param cls + * The class. + * @param methodName + * The method name. + * + * @return The result of the method invocation. + * @throws IllegalArgumentException + * If the method could not be invoked. + */ + public static Object invokeStaticMethod(final boolean throwException, final Class cls, + final String methodName) throws IllegalArgumentException { + if (cls == null || methodName == null) { + if (throwException) { + throw new IllegalArgumentException("Unexpected null argument"); + } else { + return null; + } + } + try { + return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName)); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Static method \"" + methodName + "\" could not be invoked: " + e); + } + return null; + } + } + + /** + * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException + * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If + * passed a null class reference, returns null unless throwException is true, then throws + * IllegalArgumentException. + * + * @param throwException + * Whether to throw an exception on failure. + * @param cls + * The class. + * @param methodName + * The method name. + * @param argType + * The type of the method argument. + * @param param + * The parameter value to use when invoking the method. + * + * @return The result of the method invocation. + * @throws IllegalArgumentException + * If the method could not be invoked. + */ + public static Object invokeStaticMethod(final boolean throwException, final Class cls, + final String methodName, final Class argType, final Object param) throws IllegalArgumentException { + if (cls == null || methodName == null || argType == null) { + if (throwException) { + throw new IllegalArgumentException("Unexpected null argument"); + } else { + return null; + } + } + try { + return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName, argType), + param); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Static method \"" + methodName + "\" could not be invoked: " + e); + } + return null; + } + } + + /** + * Call Class.forName(className), but return null if any exception is thrown. + * + * @param className + * The class name to load. + * @return The class of the requested name, or null if an exception was thrown while trying to load the class. + */ + public static Class classForNameOrNull(final String className) { + try { + return reflectionDriver.findClass(className); + } catch (final Throwable e) { + return null; + } + } + +} diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java new file mode 100644 index 000000000..460329c7c --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -0,0 +1,122 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.reflection; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Standard reflection driver (uses {@link AccessibleObject#setAccessible(boolean)} to access non-public fields if + * necessary). + */ +class StandardReflectionDriver extends ReflectionDriver { + @Override + Class findClass(final String className) throws Exception { + return Class.forName(className); + } + + @Override + Method[] getDeclaredMethods(final Class cls) throws Exception { + return cls.getDeclaredMethods(); + } + + @SuppressWarnings("unchecked") + @Override + Constructor[] getDeclaredConstructors(final Class cls) throws Exception { + return (Constructor[]) cls.getDeclaredConstructors(); + } + + @Override + Field[] getDeclaredFields(final Class cls) throws Exception { + return cls.getDeclaredFields(); + } + + @Override + boolean makeAccessible(final AccessibleObject obj) { + if (!obj.isAccessible()) { + try { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + obj.setAccessible(true); + return null; + } + }); + } catch (final Throwable e) { + try { + obj.setAccessible(true); + } catch (final Throwable e2) { + return false; + } + } + return obj.isAccessible(); + } + return true; + } + + @Override + Object getField(final Object object, final Field field) throws Exception { + makeAccessible(field); + return field.get(object); + } + + @Override + void setField(final Object object, final Field field, final Object value) throws Exception { + makeAccessible(field); + field.set(object, value); + } + + @Override + Object getStaticField(final Field field) throws Exception { + makeAccessible(field); + return field.get(null); + } + + @Override + void setStaticField(final Field field, final Object value) throws Exception { + makeAccessible(field); + field.set(null, value); + } + + @Override + Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { + makeAccessible(method); + return method.invoke(object, args); + } + + @Override + Object invokeStaticMethod(final Method method, final Object... args) throws Exception { + makeAccessible(method); + return method.invoke(null, args); + } +} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java deleted file mode 100644 index 6fdb53ba8..000000000 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ /dev/null @@ -1,772 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Luke Hutchison - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO - * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ -package nonapi.io.github.classgraph.utils; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import io.github.classgraph.ClassGraph; - -/** Reflection utility methods that can be used by ClassLoaderHandlers. */ -public final class ReflectionUtils { - /** The reflection driver to use. */ - private static ReflectionDriver reflectionDriver; - - static { - loadReflectionDriver(); - } - - /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ - public static void loadReflectionDriver() { - if (ClassGraph.CIRCUMVENT_ENCAPSULATION) { - try { - reflectionDriver = new NarcissusReflectionDriver(); - } catch (final Throwable t) { - System.err.println("Could not load Narcissus reflection driver: " + t); - // Fall back to standard reflection driver - } - } - if (reflectionDriver == null) { - reflectionDriver = new StandardReflectionDriver(); - } - } - - /** Reflection driver */ - private static abstract class ReflectionDriver { - /** - * Find a class by name. - * - * @param className - * the class name - * @return the class reference - */ - abstract Class findClass(final String className) throws Exception; - - /** - * Get declared methods for class. - * - * @param cls - * the class - * @return the declared methods - */ - abstract Method[] getDeclaredMethods(Class cls) throws Exception; - - /** - * Get declared constructors for class. - * - * @param - * the generic type - * @param cls - * the class - * @return the declared constructors - */ - abstract Constructor[] getDeclaredConstructors(Class cls) throws Exception; - - /** - * Get declared fields for class. - * - * @param cls - * the class - * @return the declared fields - */ - abstract Field[] getDeclaredFields(Class cls) throws Exception; - - /** - * Get the value of a non-static field, boxing the value if necessary. - * - * @param object - * the object instance to get the field value from - * @param field - * the non-static field - * @return the value of the field - */ - abstract Object getField(final Object object, final Field field) throws Exception; - - /** - * Set the value of a non-static field, unboxing the value if necessary. - * - * @param object - * the object instance to get the field value from - * @param field - * the non-static field - * @param value - * the value to set - */ - abstract void setField(final Object object, final Field field, Object value) throws Exception; - - /** - * Get the value of a static field, boxing the value if necessary. - * - * @param field - * the static field - * @return the static field - */ - abstract Object getStaticField(final Field field) throws Exception; - - /** - * Set the value of a static field, unboxing the value if necessary. - * - * @param field - * the static field - * @param the - * value to set - */ - abstract void setStaticField(final Field field, Object value) throws Exception; - - /** - * Invoke a non-static method, boxing the result if necessary. - * - * @param object - * the object instance to invoke the method on - * @param method - * the non-static method - * @param args - * the method arguments (or {@code new Object[0]} if there are no args) - * @return the return value (possibly a boxed value) - */ - abstract Object invokeMethod(final Object object, final Method method, final Object... args) - throws Exception; - - /** - * Invoke a static method, boxing the result if necessary. - * - * @param method - * the static method - * @param args - * the method arguments (or {@code new Object[0]} if there are no args) - * @return the return value (possibly a boxed value) - */ - abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; - - /** - * Make a field or method accessible. - * - * @param accessibleObject - * the field or method. - * @return true if successful. - */ - abstract boolean makeAccessible(final AccessibleObject accessibleObject); - - /** Iterator applied to each method of a class and its superclasses/interfaces. */ - private static interface MethodIterator { - /** @return true to stop iterating, or false to continue iterating */ - boolean foundMethod(Method m) throws Exception; - } - - /** - * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to - * collect all methods of the class and its superclasses, and any default methods defined in interfaces. - * - * @param cls - * the class - */ - void forAllMethods(final Class cls, final MethodIterator methodIter) throws Exception { - // Iterate from class to its superclasses, and find initial interfaces to start traversing from - final Set> visited = new HashSet<>(); - final LinkedList> interfaceQueue = new LinkedList<>(); - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Method m : getDeclaredMethods(c)) { - if (methodIter.foundMethod(m)) { - return; - } - } - // Find interfaces and superinterfaces implemented by this class or its superclasses - if (c.isInterface() && visited.add(c)) { - interfaceQueue.add(c); - } - for (final Class iface : c.getInterfaces()) { - if (visited.add(iface)) { - interfaceQueue.add(iface); - } - } - } - // Traverse through interfaces looking for default methods - while (!interfaceQueue.isEmpty()) { - final Class iface = interfaceQueue.remove(); - for (final Method m : getDeclaredMethods(iface)) { - if (methodIter.foundMethod(m)) { - return; - } - } - for (final Class superIface : iface.getInterfaces()) { - if (visited.add(superIface)) { - interfaceQueue.add(superIface); - } - } - } - } - - /** - * Find a method by name and parameter types in the given class. - * - * @param cls - * the class - * @param methodName - * the method name. - * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} - * @throws NoSuchMethodException - * if the class does not contain a method of the given name and parameter types - */ - Method findMethod(final Class cls, final String methodName, final Class... paramTypes) - throws Exception { - final AtomicReference method = new AtomicReference<>(); - forAllMethods(cls, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes()) - // If method is not accessible, fall through and try superclass method of same - // name and paramTypes - && makeAccessible(m)) { - method.set(m); - return true; - } - return false; - } - }); - final Method m = method.get(); - if (m != null) { - return m; - } else { - throw new NoSuchMethodException(methodName); - } - } - - /** - * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also - * iterates up through superclasses, to collect all methods of the class and its superclasses. - * - * @param cls - * the class - * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. - */ - List enumerateMethods(final Class cls) throws Exception { - final List methodOrder = new ArrayList<>(); - forAllMethods(cls, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - methodOrder.add(m); - return false; - } - }); - return methodOrder; - } - - /** - * Find a field by name in the given class. - * - * @param cls - * the class - * @param fieldName - * the field name. - * @return the {@link Field} - * @throws NoSuchFieldException - * if the class does not contain a field of the given name - */ - Field findField(final Class cls, final String fieldName) throws Exception { - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Field field : getDeclaredFields(c)) { - if (field.getName().equals(fieldName)) { - return field; - } - } - } - throw new NoSuchFieldException(fieldName); - } - } - - /** - * Standard reflection driver (uses {@link AccessibleObject#setAccessible(boolean)} to access non-public fields - * if necessary). - */ - private static class StandardReflectionDriver extends ReflectionDriver { - @Override - Class findClass(final String className) throws Exception { - return Class.forName(className); - } - - @Override - Method[] getDeclaredMethods(final Class cls) throws Exception { - return cls.getDeclaredMethods(); - } - - @SuppressWarnings("unchecked") - @Override - Constructor[] getDeclaredConstructors(final Class cls) throws Exception { - return (Constructor[]) cls.getDeclaredConstructors(); - } - - @Override - Field[] getDeclaredFields(final Class cls) throws Exception { - return cls.getDeclaredFields(); - } - - @Override - boolean makeAccessible(final AccessibleObject obj) { - if (!obj.isAccessible()) { - try { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - obj.setAccessible(true); - return null; - } - }); - } catch (final Throwable e) { - try { - obj.setAccessible(true); - } catch (final Throwable e2) { - return false; - } - } - return obj.isAccessible(); - } - return true; - } - - @Override - Object getField(final Object object, final Field field) throws Exception { - makeAccessible(field); - return field.get(object); - } - - @Override - void setField(final Object object, final Field field, final Object value) throws Exception { - makeAccessible(field); - field.set(object, value); - } - - @Override - Object getStaticField(final Field field) throws Exception { - makeAccessible(field); - return field.get(null); - } - - @Override - void setStaticField(final Field field, final Object value) throws Exception { - makeAccessible(field); - field.set(null, value); - } - - @Override - Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - makeAccessible(method); - return method.invoke(object, args); - } - - @Override - Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - makeAccessible(method); - return method.invoke(null, args); - } - } - - /** - * Narcissus reflection driver (uses the Narcissus - * library, if it is available, which allows access to non-public fields and methods, circumventing - * encapsulation and visibility controls via JNI). - */ - private static class NarcissusReflectionDriver extends ReflectionDriver { - private final Map> methodNameToMethods = new HashMap<>(); - private final Class narcissusClass; - private final Method getDeclaredMethods; - private final Method findClass; - private final Method getDeclaredConstructors; - private final Method getDeclaredFields; - private final Method getField; - private final Method setField; - private final Method getStaticField; - private final Method setStaticField; - private final Method invokeMethod; - private final Method invokeStaticMethod; - - private Method findIndexedMethod(final String methodName, final Class... paramTypes) - throws NoSuchMethodException { - final List methods = methodNameToMethods.get(methodName); - if (methods != null) { - for (final Method method : methods) { - if (Arrays.equals(method.getParameterTypes(), paramTypes)) { - return method; - } - } - } - throw new NoSuchMethodException(methodName); - } - - private void indexMethods(final List methods) { - // Index Narcissus methods by name - for (final Method method : methods) { - List methodsForName = methodNameToMethods.get(method.getName()); - if (methodsForName == null) { - methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); - } - methodsForName.add(method); - } - } - - NarcissusReflectionDriver() throws Exception { - // Load Narcissus class via reflection, so that there is no runtime dependency - final StandardReflectionDriver drv = new StandardReflectionDriver(); - narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); - if (!(Boolean) drv.getStaticField(drv.findField(narcissusClass, "libraryLoaded"))) { - throw new IllegalArgumentException("Could not load Narcissus native library"); - } - - // Look up needed methods - indexMethods(drv.enumerateMethods(narcissusClass)); - findClass = findIndexedMethod("findClass", String.class); - getDeclaredMethods = findIndexedMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findIndexedMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findIndexedMethod("getDeclaredFields", Class.class); - getField = findIndexedMethod("getField", Object.class, Field.class); - setField = findIndexedMethod("setField", Object.class, Field.class, Object.class); - getStaticField = findIndexedMethod("getStaticField", Field.class); - setStaticField = findIndexedMethod("setStaticField", Field.class, Object.class); - invokeMethod = findIndexedMethod("invokeMethod", Object.class, Method.class, Object[].class); - invokeStaticMethod = findIndexedMethod("invokeStaticMethod", Method.class, Object[].class); - } - - @Override - boolean makeAccessible(final AccessibleObject accessibleObject) { - return true; - } - - @Override - Class findClass(final String className) throws Exception { - return (Class) findClass.invoke(null, className); - } - - @Override - Method[] getDeclaredMethods(final Class cls) throws Exception { - return (Method[]) getDeclaredMethods.invoke(null, cls); - } - - @SuppressWarnings("unchecked") - @Override - Constructor[] getDeclaredConstructors(final Class cls) throws Exception { - return (Constructor[]) getDeclaredConstructors.invoke(null, cls); - } - - @Override - Field[] getDeclaredFields(final Class cls) throws Exception { - return (Field[]) getDeclaredFields.invoke(null, cls); - } - - @Override - Object getField(final Object object, final Field field) throws Exception { - return getField.invoke(null, object, field); - } - - @Override - void setField(final Object object, final Field field, final Object value) throws Exception { - setField.invoke(null, object, field, value); - } - - @Override - Object getStaticField(final Field field) throws Exception { - return getStaticField.invoke(null, field); - } - - @Override - void setStaticField(final Field field, final Object value) throws Exception { - setStaticField.invoke(null, field, value); - } - - @Override - Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(null, object, method, args); - } - - @Override - Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - return invokeStaticMethod.invoke(null, method, args); - } - } - - /** - * Constructor. - */ - private ReflectionUtils() { - // Cannot be constructed - } - - /** - * Get the value of the named field in the class of the given object or any of its superclasses. If an exception - * is thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown - * wrapping the cause, otherwise this will return null. If passed a null object, returns null unless - * throwException is true, then throws IllegalArgumentException. - * - * @param obj - * The object. - * @param fieldName - * The field name. - * @param throwException - * If true, throw an exception if the field value could not be read. - * @return The field value. - * @throws IllegalArgumentException - * If the field value could not be read. - */ - public static Object getFieldVal(final Object obj, final String fieldName, final boolean throwException) - throws IllegalArgumentException { - if (obj == null || fieldName == null) { - if (throwException) { - throw new NullPointerException(); - } else { - return null; - } - } - try { - return reflectionDriver.getField(obj, reflectionDriver.findField(obj.getClass(), fieldName)); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException( - "Can't read field " + obj.getClass().getName() + "." + fieldName + ": " + e); - } - } - return null; - } - - /** - * Get the value of the named static field in the given class or any of its superclasses. If an exception is - * thrown while trying to read the field value, and throwException is true, then IllegalArgumentException is - * thrown wrapping the cause, otherwise this will return null. If passed a null class reference, returns null - * unless throwException is true, then throws IllegalArgumentException. - * - * @param cls - * The class. - * @param fieldName - * The field name. - * @param throwException - * If true, throw an exception if the field value could not be read. - * @return The field value. - * @throws IllegalArgumentException - * If the field value could not be read. - */ - public static Object getStaticFieldVal(final Class cls, final String fieldName, final boolean throwException) - throws IllegalArgumentException { - if (cls == null || fieldName == null) { - if (throwException) { - throw new NullPointerException(); - } else { - return null; - } - } - try { - return reflectionDriver.getStaticField(reflectionDriver.findField(cls, fieldName)); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException( - "Can't read static field " + cls.getName() + "." + fieldName + ": " + e); - } - } - return null; - } - - /** - * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to - * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, - * otherwise this will return null. If passed a null object, returns null unless throwException is true, then - * throws IllegalArgumentException. - * - * @param obj - * The object. - * @param methodName - * The method name. - * @param throwException - * If true, throw an exception if the field value could not be read. - * @return The result of the method invocation. - * @throws IllegalArgumentException - * If the method could not be invoked. - */ - public static Object invokeMethod(final Object obj, final String methodName, final boolean throwException) - throws IllegalArgumentException { - if (obj == null || methodName == null) { - if (throwException) { - throw new IllegalArgumentException("Unexpected null argument"); - } else { - return null; - } - } - try { - return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName)); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); - } - return null; - } - } - - /** - * Invoke the named method in the given object or its superclasses. If an exception is thrown while trying to - * call the method, and throwException is true, then IllegalArgumentException is thrown wrapping the cause, - * otherwise this will return null. If passed a null object, returns null unless throwException is true, then - * throws IllegalArgumentException. - * - * @param obj - * The object. - * @param methodName - * The method name. - * @param argType - * The type of the method argument. - * @param param - * The parameter value to use when invoking the method. - * @param throwException - * Whether to throw an exception on failure. - * @return The result of the method invocation. - * @throws IllegalArgumentException - * If the method could not be invoked. - */ - public static Object invokeMethod(final Object obj, final String methodName, final Class argType, - final Object param, final boolean throwException) throws IllegalArgumentException { - if (obj == null || methodName == null || argType == null) { - if (throwException) { - throw new IllegalArgumentException("Unexpected null argument"); - } else { - return null; - } - } - try { - return reflectionDriver.invokeMethod(obj, - reflectionDriver.findMethod(obj.getClass(), methodName, argType), param); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); - } - return null; - } - } - - /** - * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException - * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If - * passed a null class reference, returns null unless throwException is true, then throws - * IllegalArgumentException. - * - * @param cls - * The class. - * @param methodName - * The method name. - * @param throwException - * Whether to throw an exception on failure. - * @return The result of the method invocation. - * @throws IllegalArgumentException - * If the method could not be invoked. - */ - public static Object invokeStaticMethod(final Class cls, final String methodName, - final boolean throwException) throws IllegalArgumentException { - if (cls == null || methodName == null) { - if (throwException) { - throw new IllegalArgumentException("Unexpected null argument"); - } else { - return null; - } - } - try { - return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName)); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException( - "Static method \"" + methodName + "\" could not be invoked: " + e); - } - return null; - } - } - - /** - * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException - * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If - * passed a null class reference, returns null unless throwException is true, then throws - * IllegalArgumentException. - * - * @param cls - * The class. - * @param methodName - * The method name. - * @param argType - * The type of the method argument. - * @param param - * The parameter value to use when invoking the method. - * @param throwException - * Whether to throw an exception on failure. - * @return The result of the method invocation. - * @throws IllegalArgumentException - * If the method could not be invoked. - */ - public static Object invokeStaticMethod(final Class cls, final String methodName, final Class argType, - final Object param, final boolean throwException) throws IllegalArgumentException { - if (cls == null || methodName == null || argType == null) { - if (throwException) { - throw new IllegalArgumentException("Unexpected null argument"); - } else { - return null; - } - } - try { - return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName, argType), - param); - } catch (final Throwable e) { - if (throwException) { - throw new IllegalArgumentException( - "Static method \"" + methodName + "\" could not be invoked: " + e); - } - return null; - } - } - - /** - * Call Class.forName(className), but return null if any exception is thrown. - * - * @param className - * The class name to load. - * @return The class of the requested name, or null if an exception was thrown while trying to load the class. - */ - public static Class classForNameOrNull(final String className) { - try { - return reflectionDriver.findClass(className); - } catch (final Throwable e) { - return null; - } - } - -} diff --git a/src/test/java/io/github/classgraph/features/NarcissusTest.java b/src/test/java/io/github/classgraph/features/NarcissusTest.java index 5f8637e71..bcaa08dd4 100644 --- a/src/test/java/io/github/classgraph/features/NarcissusTest.java +++ b/src/test/java/io/github/classgraph/features/NarcissusTest.java @@ -6,7 +6,7 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.utils.ReflectionUtils; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** * NarcissusTest. @@ -17,7 +17,7 @@ class NarcissusTest { void annotationEquality() { ClassGraph.CIRCUMVENT_ENCAPSULATION = true; ReflectionUtils.loadReflectionDriver(); - assertThat(ReflectionUtils.getStaticFieldVal(ReflectionUtils.class, "reflectionDriver", true).getClass() + assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); try (ScanResult scanResult = new ClassGraph().acceptPackages(NarcissusTest.class.getPackage().getName()) .enableAllInfo().scan()) { diff --git a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java index 2014d8fb5..b0e37cd95 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -50,9 +50,10 @@ * Issue193Test. */ public class Issue420Test { - static { + static { ClassGraph.CIRCUMVENT_ENCAPSULATION = true; } + /** * Test accessing a jar over Jimfs. * From ce50c6bab6de0a7e17cc11729e0302033963c064 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 15:17:00 -0600 Subject: [PATCH 158/713] Fix test --- .../issues/issue420/Issue420Test.java | 4 ---- .../classgraph/issues/issue400/Issue400.java | 20 +++++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java index b0e37cd95..8e53da1eb 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -50,10 +50,6 @@ * Issue193Test. */ public class Issue420Test { - static { - ClassGraph.CIRCUMVENT_ENCAPSULATION = true; - } - /** * Test accessing a jar over Jimfs. * diff --git a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java index 8cb370b6b..ece4090ff 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -57,15 +57,19 @@ private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(final URL... j } final long ramAtEnd = usedRam(); - assertThat(ramAtStart) - .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: " - + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB) - .isCloseTo(ramAfterScan, offset(MEMORY_TOLERANCE)); + if (ramAtStart < ramAfterScan) { + assertThat(ramAtStart) + .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: " + + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB) + .isCloseTo(ramAfterScan, offset(MEMORY_TOLERANCE)); + } - assertThat(ramAtStart) - .withFailMessage("Memory usage after cleaning up should stay within reasonable range: " - + "went from %s to %s MB.", ramAtStart / MB, ramAtEnd / MB) - .isCloseTo(ramAtEnd, offset(MEMORY_TOLERANCE)); + if (ramAtStart < ramAtEnd) { + assertThat(ramAtStart) + .withFailMessage("Memory usage after cleaning up should stay within reasonable range: " + + "went from %s to %s MB.", ramAtStart / MB, ramAtEnd / MB) + .isCloseTo(ramAtEnd, offset(MEMORY_TOLERANCE)); + } } /** From 018f69cc9ac80a99e1281f7f22ad703815f19e33 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 15:43:39 -0600 Subject: [PATCH 159/713] Add jvm-driver as another runtime method for circumventing encapsulation --- pom.xml | 23 ++- .../java/io/github/classgraph/ClassGraph.java | 38 +++- .../reflection/JVMDriverReflectionDriver.java | 176 ++++++++++++++++++ .../reflection/NarcissusReflectionDriver.java | 52 ++---- .../reflection/ReflectionDriver.java | 47 ++++- .../reflection/ReflectionUtils.java | 10 +- .../EncapsulationCircumventionTest.java | 49 +++++ .../classgraph/features/NarcissusTest.java | 30 --- 8 files changed, 344 insertions(+), 81 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java create mode 100644 src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java delete mode 100644 src/test/java/io/github/classgraph/features/NarcissusTest.java diff --git a/pom.xml b/pom.xml index aa87c6f6d..6e271b230 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -72,6 +75,12 @@ 1.0.6 true + + io.github.toolfactory + jvm-driver + 7.1.4 + true + @@ -276,7 +285,8 @@ - + org.codehaus.mojo.signature java17 @@ -355,8 +365,13 @@ - - + + diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index f655baa5e..88ffa9ebc 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.InputStream; +import java.lang.reflect.AccessibleObject; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; @@ -80,21 +81,50 @@ public class ClassGraph { Runtime.getRuntime().availableProcessors() * 1.25) // ); + /** + * Method to use to attempt to circumvent encapsulation in JDK 16+, in order to get access to a classloader's + * private classpath. + */ + public enum CircumventEncapsulationMethod { + /** + * Use the reflection API and {@link AccessibleObject#setAccessible(boolean)} to try to gain access to + * private classpath fields or methods in order to determine the classpath. + */ + NONE, + + /** + * Use the Narcissus library to try to gain access to + * private classloader fields or methods in order to determine the classpath. + */ + NARCISSUS, + + /** + * Use the JVM-Driver library to try to gain access + * to private classloader fields or methods in order to determine the classpath. + */ + JVM_DRIVER; + } + /** * If you are running on JDK 16+, the JDK enforces strong encapsulation, and ClassGraph may be unable to read * the classpath from your classloader if the classloader does not make the classpath available via a public * method or field. * *

- * To enable a workaround to this, set this static field to to true before interacting with ClassGraph in any - * other way, and also include the Narcissus library on - * the classpath or module path. + * To enable a workaround to this, set this static field to {@link CircumventEncapsulationMethod#NARCISSUS} or + * {@link CircumventEncapsulationMethod#JVM_DRIVER} before interacting with ClassGraph in any other way, and + * also include the Narcissus or + * JVM-Driver library respectively on the classpath or + * module path. * *

* Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a native * code library, and is currently only compiled for Linux x86/x64, Windows x86/x64, and Mac OS X x64 bit. + * + *

+ * JVM-Driver uses a pure JVM solution to try to circumvent encapsulation and security controls. */ - public static boolean CIRCUMVENT_ENCAPSULATION = false; + public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; /** * If non-null, log while scanning. diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java new file mode 100644 index 000000000..abdd1b15c --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -0,0 +1,176 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.reflection; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Reflection driver for the jvm-driver library, if it is + * available at runtime. This library allows access to non-public fields and methods, circumventing encapsulation + * and visibility controls, via a range of JVM-native tactics. + */ +class JVMDriverReflectionDriver extends ReflectionDriver { + private Object driver; + private final Method getDeclaredMethods; + private final Method getDeclaredConstructors; + private final Method getDeclaredFields; + private final Method getField; + private final Method setField; + private final Method invokeMethod; + private final Method setAccessibleMethod; + + private static interface ClassFinder { + Class findClass(String className) throws Exception; + } + + private ClassFinder classFinder; + + JVMDriverReflectionDriver() throws Exception { + // Construct a jvm-driver Driver instance via reflection, so that there is no runtime dependency + final StandardReflectionDriver drv = new StandardReflectionDriver(); + // driverClass = drv.findClass("io.github.toolfactory.jvm.Driver"); + // Class driverFactoryClass = drv.findClass("io.github.toolfactory.jvm.Driver$Factory"); + // Object driverInstance = drv.invokeStaticMethod(drv.findMethod(driverFactoryClass, "getNew")); + // if (driverInstance == null) { + // throw new IllegalArgumentException("Could not load jvm-driver library"); + // } + Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); + driver = driverClass.newInstance(); + + // Look up needed methods + indexMethods(drv.enumerateDriverMethods(driverClass)); + getDeclaredMethods = findDriverMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findDriverMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findDriverMethod("getDeclaredFields", Class.class); + getField = findDriverMethod("getFieldValue", Object.class, Field.class); + setField = findDriverMethod("setFieldValue", Object.class, Field.class, Object.class); + invokeMethod = findDriverMethod("invoke", Method.class, Object.class, Object[].class); + setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); + try { + // JDK 8 + Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, + ClassLoader.class); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + classFinder = new ClassFinder() { + @Override + public Class findClass(String className) throws Exception { + return (Class) forName0_method.invoke(null, className, true, classLoader); + } + }; + } catch (Throwable t1) { + // JDK 16 + try { + Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, + ClassLoader.class, Class.class); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + classFinder = new ClassFinder() { + @Override + public Class findClass(String className) throws Exception { + return (Class) forName0_method.invoke(null, className, true, classLoader, + JVMDriverReflectionDriver.class); + } + }; + } catch (Throwable t2) { + // Fallback if the above fails: just use Class.forName. + // This won't find private non-exported classes in other modules. + Method forName_method = findMethod(Class.class, "forName", String.class); + classFinder = new ClassFinder() { + @Override + public Class findClass(String className) throws Exception { + return (Class) forName_method.invoke(null, className); + } + }; + } + } + } + + @Override + boolean makeAccessible(final AccessibleObject accessibleObject) { + try { + setAccessibleMethod.invoke(driver, accessibleObject, true); + } catch (Throwable t) { + return false; + } + return true; + } + + @Override + Class findClass(final String className) throws Exception { + return classFinder.findClass(className); + } + + @Override + Method[] getDeclaredMethods(final Class cls) throws Exception { + return (Method[]) getDeclaredMethods.invoke(driver, cls); + } + + @SuppressWarnings("unchecked") + @Override + Constructor[] getDeclaredConstructors(final Class cls) throws Exception { + return (Constructor[]) getDeclaredConstructors.invoke(driver, cls); + } + + @Override + Field[] getDeclaredFields(final Class cls) throws Exception { + return (Field[]) getDeclaredFields.invoke(driver, cls); + } + + @Override + Object getField(final Object object, final Field field) throws Exception { + return getField.invoke(driver, object, field); + } + + @Override + void setField(final Object object, final Field field, final Object value) throws Exception { + setField.invoke(driver, object, field, value); + } + + @Override + Object getStaticField(final Field field) throws Exception { + return getField.invoke(driver, null, field); + } + + @Override + void setStaticField(final Field field, final Object value) throws Exception { + setField.invoke(driver, null, field, value); + } + + @Override + Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { + return invokeMethod.invoke(driver, method, object, args); + } + + @Override + Object invokeStaticMethod(final Method method, final Object... args) throws Exception { + return invokeMethod.invoke(driver, method, null, args); + } +} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java index a4a7ca7b7..f23deeabe 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -32,11 +32,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * Narcissus reflection driver (uses the Narcissus library, @@ -44,7 +39,6 @@ * visibility controls via JNI). */ class NarcissusReflectionDriver extends ReflectionDriver { - private final Map> methodNameToMethods = new HashMap<>(); private final Class narcissusClass; private final Method getDeclaredMethods; private final Method findClass; @@ -57,30 +51,6 @@ class NarcissusReflectionDriver extends ReflectionDriver { private final Method invokeMethod; private final Method invokeStaticMethod; - private Method findIndexedMethod(final String methodName, final Class... paramTypes) - throws NoSuchMethodException { - final List methods = methodNameToMethods.get(methodName); - if (methods != null) { - for (final Method method : methods) { - if (Arrays.equals(method.getParameterTypes(), paramTypes)) { - return method; - } - } - } - throw new NoSuchMethodException(methodName); - } - - private void indexMethods(final List methods) { - // Index Narcissus methods by name - for (final Method method : methods) { - List methodsForName = methodNameToMethods.get(method.getName()); - if (methodsForName == null) { - methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); - } - methodsForName.add(method); - } - } - NarcissusReflectionDriver() throws Exception { // Load Narcissus class via reflection, so that there is no runtime dependency final StandardReflectionDriver drv = new StandardReflectionDriver(); @@ -90,17 +60,17 @@ private void indexMethods(final List methods) { } // Look up needed methods - indexMethods(drv.enumerateMethods(narcissusClass)); - findClass = findIndexedMethod("findClass", String.class); - getDeclaredMethods = findIndexedMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findIndexedMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findIndexedMethod("getDeclaredFields", Class.class); - getField = findIndexedMethod("getField", Object.class, Field.class); - setField = findIndexedMethod("setField", Object.class, Field.class, Object.class); - getStaticField = findIndexedMethod("getStaticField", Field.class); - setStaticField = findIndexedMethod("setStaticField", Field.class, Object.class); - invokeMethod = findIndexedMethod("invokeMethod", Object.class, Method.class, Object[].class); - invokeStaticMethod = findIndexedMethod("invokeStaticMethod", Method.class, Object[].class); + indexMethods(drv.enumerateDriverMethods(narcissusClass)); + findClass = findDriverMethod("findClass", String.class); + getDeclaredMethods = findDriverMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findDriverMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findDriverMethod("getDeclaredFields", Class.class); + getField = findDriverMethod("getField", Object.class, Field.class); + setField = findDriverMethod("setField", Object.class, Field.class, Object.class); + getStaticField = findDriverMethod("getStaticField", Field.class); + setStaticField = findDriverMethod("setStaticField", Field.class, Object.class); + invokeMethod = findDriverMethod("invokeMethod", Object.class, Method.class, Object[].class); + invokeStaticMethod = findDriverMethod("invokeStaticMethod", Method.class, Object[].class); } @Override diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 9c9d69f6a..adad5a53e 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -34,14 +34,18 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; /** Reflection driver */ abstract class ReflectionDriver { + private final Map> methodNameToMethods = new HashMap<>(); + /** * Find a class by name. * @@ -161,6 +165,47 @@ private static interface MethodIterator { boolean foundMethod(Method m) throws Exception; } + /** + * Find an indexed method. + * + * @param methodName + * The method name. + * @param paramTypes + * The parameter types. + * @return The method, if found. + * @throws NoSuchMethodException + * If not found. + */ + protected Method findDriverMethod(final String methodName, final Class... paramTypes) + throws NoSuchMethodException { + final List methods = methodNameToMethods.get(methodName); + if (methods != null) { + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + return method; + } + } + } + throw new NoSuchMethodException(methodName); + } + + /** + * Index a list of methods. + * + * @param methods + * The methods to index. + */ + protected void indexMethods(final List methods) { + // Index Narcissus methods by name + for (final Method method : methods) { + List methodsForName = methodNameToMethods.get(method.getName()); + if (methodsForName == null) { + methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); + } + methodsForName.add(method); + } + } + /** * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to * collect all methods of the class and its superclasses, and any default methods defined in interfaces. @@ -248,7 +293,7 @@ && makeAccessible(m)) { * the class * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. */ - List enumerateMethods(final Class cls) throws Exception { + List enumerateDriverMethods(final Class cls) throws Exception { final List methodOrder = new ArrayList<>(); forAllMethods(cls, new MethodIterator() { @Override diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index e7c5134bf..ec250e3e0 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.reflection; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassGraph.CircumventEncapsulationMethod; /** Reflection utility methods that can be used by ClassLoaderHandlers. */ public final class ReflectionUtils { @@ -41,13 +42,20 @@ public final class ReflectionUtils { /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ public static void loadReflectionDriver() { - if (ClassGraph.CIRCUMVENT_ENCAPSULATION) { + if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NARCISSUS) { try { reflectionDriver = new NarcissusReflectionDriver(); } catch (final Throwable t) { System.err.println("Could not load Narcissus reflection driver: " + t); // Fall back to standard reflection driver } + } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER) { + try { + reflectionDriver = new JVMDriverReflectionDriver(); + } catch (final Throwable t) { + System.err.println("Could not load JVM-Driver reflection driver: " + t); + // Fall back to standard reflection driver + } } if (reflectionDriver == null) { reflectionDriver = new StandardReflectionDriver(); diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java new file mode 100644 index 000000000..e7e2509ac --- /dev/null +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -0,0 +1,49 @@ +package io.github.classgraph.features; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassGraph.CircumventEncapsulationMethod; +import io.github.classgraph.ScanResult; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; + +/** + * Encapsulation circumvention test. + */ +class EncapsulationCircumventionTest { + /** Test Narcissus. */ + @Test + void testNarcissus() { + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS; + ReflectionUtils.loadReflectionDriver(); + assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() + .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() + .scan()) { + assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); + } finally { + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; + ReflectionUtils.loadReflectionDriver(); + } + } + + /** Test JVM-Driver. */ + @Test + void testJVMDriver() { + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; + ReflectionUtils.loadReflectionDriver(); + assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() + .getSimpleName()).isEqualTo("JVMDriverReflectionDriver"); + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() + .scan()) { + assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); + } finally { + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; + ReflectionUtils.loadReflectionDriver(); + } + } +} diff --git a/src/test/java/io/github/classgraph/features/NarcissusTest.java b/src/test/java/io/github/classgraph/features/NarcissusTest.java deleted file mode 100644 index bcaa08dd4..000000000 --- a/src/test/java/io/github/classgraph/features/NarcissusTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.classgraph.features; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; - -/** - * NarcissusTest. - */ -class NarcissusTest { - /** Test Narcissus was able to start up OK. */ - @Test - void annotationEquality() { - ClassGraph.CIRCUMVENT_ENCAPSULATION = true; - ReflectionUtils.loadReflectionDriver(); - assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() - .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); - try (ScanResult scanResult = new ClassGraph().acceptPackages(NarcissusTest.class.getPackage().getName()) - .enableAllInfo().scan()) { - assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); - } finally { - ClassGraph.CIRCUMVENT_ENCAPSULATION = false; - ReflectionUtils.loadReflectionDriver(); - } - } -} From 3b07db80e909814814a08e7fdadf8493b3341f3a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 15:48:13 -0600 Subject: [PATCH 160/713] Source > Cleanup --- .../reflection/JVMDriverReflectionDriver.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index abdd1b15c..0b789fe02 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -63,7 +63,7 @@ private static interface ClassFinder { // if (driverInstance == null) { // throw new IllegalArgumentException("Could not load jvm-driver library"); // } - Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); + final Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); driver = driverClass.newInstance(); // Look up needed methods @@ -77,35 +77,35 @@ private static interface ClassFinder { setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); try { // JDK 8 - Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, + final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class); - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override - public Class findClass(String className) throws Exception { + public Class findClass(final String className) throws Exception { return (Class) forName0_method.invoke(null, className, true, classLoader); } }; - } catch (Throwable t1) { + } catch (final Throwable t1) { // JDK 16 try { - Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, + final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class, Class.class); - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override - public Class findClass(String className) throws Exception { + public Class findClass(final String className) throws Exception { return (Class) forName0_method.invoke(null, className, true, classLoader, JVMDriverReflectionDriver.class); } }; - } catch (Throwable t2) { + } catch (final Throwable t2) { // Fallback if the above fails: just use Class.forName. // This won't find private non-exported classes in other modules. - Method forName_method = findMethod(Class.class, "forName", String.class); + final Method forName_method = findMethod(Class.class, "forName", String.class); classFinder = new ClassFinder() { @Override - public Class findClass(String className) throws Exception { + public Class findClass(final String className) throws Exception { return (Class) forName_method.invoke(null, className); } }; @@ -117,7 +117,7 @@ public Class findClass(String className) throws Exception { boolean makeAccessible(final AccessibleObject accessibleObject) { try { setAccessibleMethod.invoke(driver, accessibleObject, true); - } catch (Throwable t) { + } catch (final Throwable t) { return false; } return true; From 012d193266ab4036673decbc5d9cb5414603134f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 16:20:57 -0600 Subject: [PATCH 161/713] Update to jvm-driver 7.1.5 --- pom.xml | 2 +- .../classgraph/reflection/JVMDriverReflectionDriver.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 6e271b230..b0a64cd37 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ io.github.toolfactory jvm-driver - 7.1.4 + 7.1.5 true diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 0b789fe02..b44a8d6fd 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -73,7 +73,7 @@ private static interface ClassFinder { getDeclaredFields = findDriverMethod("getDeclaredFields", Class.class); getField = findDriverMethod("getFieldValue", Object.class, Field.class); setField = findDriverMethod("setFieldValue", Object.class, Field.class, Object.class); - invokeMethod = findDriverMethod("invoke", Method.class, Object.class, Object[].class); + invokeMethod = findDriverMethod("invoke", Object.class, Method.class, Object[].class); setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); try { // JDK 8 @@ -166,11 +166,11 @@ void setStaticField(final Field field, final Object value) throws Exception { @Override Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(driver, method, object, args); + return invokeMethod.invoke(driver, object, method, args); } @Override Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(driver, method, null, args); + return invokeMethod.invoke(driver, null, method, args); } } \ No newline at end of file From 3bfb0bd1d42651ee70217838ec81b2807be65864 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 16:21:23 -0600 Subject: [PATCH 162/713] [maven-release-plugin] prepare release classgraph-4.8.124 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index b0a64cd37..8eeeb5e10 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.124-SNAPSHOT + 4.8.124 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.124 @@ -285,8 +282,7 @@ - + org.codehaus.mojo.signature java17 @@ -365,13 +361,8 @@ - - + + From 451a08404627bd50e0acfec991a5c78282591d49 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 16:21:25 -0600 Subject: [PATCH 163/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8eeeb5e10..bcae2ebb4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.124 + 4.8.125-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.124 + classgraph-4.8.118 From f46eed111c7a6065ca2b65ce35926ee4c54d974c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 6 Oct 2021 16:31:29 -0600 Subject: [PATCH 164/713] Update README --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bf98a0159..4d9afc920 100644 --- a/README.md +++ b/README.md @@ -115,18 +115,20 @@ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That wi **If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem.** -You can circumvent this restriction by: +ClassGraph can use either of the following libraries to silently circumvent all of Java's security mechanisms (visibility/access checks, security manager restrictions, and strong encapsulation), in order to read the classpath from private fields and methods of classloaders. -* Upgrading ClassGraph to at least version 4.8.123 -* Adding the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). -* Setting `ClassGraph.CIRCUMVENT_ENCAPSULATION = true;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). +* Narcissus by Luke Hutchison (@lukehutch), author of ClassGraph +* JVM-Driver is by Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core). -ClassGraph uses Narcissus to silently circumvent all of Java's security mechanisms (visibility/access checks, security manager restrictions, and strong encapsulation) by using the JNI API, in order to read the classpath from private fields and methods of classloaders. +To use one of these libraries: -Narcissus is a collaboration between: - -* Luke Hutchison (@lukehutch), author of ClassGraph -* Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core) and [toolfactory/jvm-driver](https://github.com/toolfactory/jvm-driver), which is an alternative to Narcissus +* Upgrade ClassGraph to at least version 4.8.124 +* Either: + 1. Add the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (this includes a native library, and only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). + 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulation.NARCISSUS;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). +* Or: + 1. Add the [JVM-Driver](https://github.com/toolfactory/jvm-driver) library to your project as an extra dependency (this uses only Java code, but only works to circumvent encapsulation without native code in JDK 16 currently). + 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulation.JVM_DRIVER;` before interacting with ClassGraph in any other way (this will load the JVM-Driver library as ClassGraph's reflection driver). JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment maintainers to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** From d489714526d5f61bfe0d4347e10f8a09670b106e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 01:25:50 -0600 Subject: [PATCH 165/713] Set CIRCUMVENT_ENCAPSULATION to NONE by default (#568) --- src/main/java/io/github/classgraph/ClassGraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 88ffa9ebc..45d51653d 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -124,7 +124,7 @@ public enum CircumventEncapsulationMethod { *

* JVM-Driver uses a pure JVM solution to try to circumvent encapsulation and security controls. */ - public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; + public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; /** * If non-null, log while scanning. From 6b5481949094f3233739c2ab61dd2f40df171da8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 01:26:14 -0600 Subject: [PATCH 166/713] [maven-release-plugin] prepare release classgraph-4.8.125 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bcae2ebb4..071e484dc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.125-SNAPSHOT + 4.8.125 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.125 From 0682df0e067cc1662f9d722afde7a1c27eb974dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 01:26:16 -0600 Subject: [PATCH 167/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 071e484dc..3c915d723 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.125 + 4.8.126-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.125 + classgraph-4.8.118 From 78a439ef99fe2f018eb2bd3d7a6c1994da980234 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 18:16:11 -0600 Subject: [PATCH 168/713] Fix error in documentation (#569) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d9afc920..c1567d7f9 100644 --- a/README.md +++ b/README.md @@ -125,10 +125,10 @@ To use one of these libraries: * Upgrade ClassGraph to at least version 4.8.124 * Either: 1. Add the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (this includes a native library, and only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). - 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulation.NARCISSUS;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). + 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). * Or: 1. Add the [JVM-Driver](https://github.com/toolfactory/jvm-driver) library to your project as an extra dependency (this uses only Java code, but only works to circumvent encapsulation without native code in JDK 16 currently). - 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulation.JVM_DRIVER;` before interacting with ClassGraph in any other way (this will load the JVM-Driver library as ClassGraph's reflection driver). + 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER;` before interacting with ClassGraph in any other way (this will load the JVM-Driver library as ClassGraph's reflection driver). JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment maintainers to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** From b162e5c4a048bbff772e2bbdb8697bd3d55c2894 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Oct 2021 02:00:59 +0000 Subject: [PATCH 169/713] Bump jvm-driver from 7.1.5 to 7.3.0 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 7.1.5 to 7.3.0. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-7.1.5...jvm-driver-7.3.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c915d723..9ae270bcd 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 7.1.5 + 7.3.0 true From f303cec67e3d64d9eff8b09bed9f8c0fb59c7ef8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 23:41:13 -0600 Subject: [PATCH 170/713] Fix for Class.forName for IBM Semeru --- .../reflection/JVMDriverReflectionDriver.java | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index b44a8d6fd..791546d6e 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -87,8 +87,11 @@ public Class findClass(final String className) throws Exception { } }; } catch (final Throwable t1) { - // JDK 16 + // Fall through + } + if (classFinder == null) { try { + // JDK 16 final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class, Class.class); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -100,17 +103,36 @@ public Class findClass(final String className) throws Exception { } }; } catch (final Throwable t2) { - // Fallback if the above fails: just use Class.forName. - // This won't find private non-exported classes in other modules. - final Method forName_method = findMethod(Class.class, "forName", String.class); + // Fall through + } + } + if (classFinder == null) { + try { + // IBM Semeru + final Method forNameImpl_method = findMethod(Class.class, "forNameImpl", String.class, + boolean.class, ClassLoader.class); + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { - return (Class) forName_method.invoke(null, className); + return (Class) forNameImpl_method.invoke(null, className, true, classLoader); } }; + } catch (final Throwable t2) { + // Fall through } } + if (classFinder == null) { + // Fallback if the above fails: just use Class.forName. + // This won't find private non-exported classes in other modules. + final Method forName_method = findMethod(Class.class, "forName", String.class); + classFinder = new ClassFinder() { + @Override + public Class findClass(final String className) throws Exception { + return (Class) forName_method.invoke(null, className); + } + }; + } } @Override From 161f010ec20fcbe6902c877801b0e1eaa490f502 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 23:43:06 -0600 Subject: [PATCH 171/713] Update comments --- .../classgraph/reflection/JVMDriverReflectionDriver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 791546d6e..d3d6630da 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -76,7 +76,7 @@ private static interface ClassFinder { invokeMethod = findDriverMethod("invoke", Object.class, Method.class, Object[].class); setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); try { - // JDK 8 + // JDK 7 and 8 final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -91,7 +91,7 @@ public Class findClass(final String className) throws Exception { } if (classFinder == null) { try { - // JDK 16 + // JDK 16 (and possibly earlier) final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class, Class.class); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); From 2aee2108b2500b1acbf8ce1566d20f60865060ae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 23:44:22 -0600 Subject: [PATCH 172/713] Update variable name --- .../github/classgraph/reflection/JVMDriverReflectionDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index d3d6630da..ec8f27613 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -59,7 +59,7 @@ private static interface ClassFinder { final StandardReflectionDriver drv = new StandardReflectionDriver(); // driverClass = drv.findClass("io.github.toolfactory.jvm.Driver"); // Class driverFactoryClass = drv.findClass("io.github.toolfactory.jvm.Driver$Factory"); - // Object driverInstance = drv.invokeStaticMethod(drv.findMethod(driverFactoryClass, "getNew")); + // driver = drv.invokeStaticMethod(drv.findMethod(driverFactoryClass, "getNew")); // if (driverInstance == null) { // throw new IllegalArgumentException("Could not load jvm-driver library"); // } From 4ae178b65ad63a2f1b5f5cc1293828ec5c717ac1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Oct 2021 23:48:16 -0600 Subject: [PATCH 173/713] Update variable names --- .../classgraph/reflection/JVMDriverReflectionDriver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index ec8f27613..22d852589 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -86,7 +86,7 @@ public Class findClass(final String className) throws Exception { return (Class) forName0_method.invoke(null, className, true, classLoader); } }; - } catch (final Throwable t1) { + } catch (final Throwable t) { // Fall through } if (classFinder == null) { @@ -102,7 +102,7 @@ public Class findClass(final String className) throws Exception { JVMDriverReflectionDriver.class); } }; - } catch (final Throwable t2) { + } catch (final Throwable t) { // Fall through } } @@ -118,7 +118,7 @@ public Class findClass(final String className) throws Exception { return (Class) forNameImpl_method.invoke(null, className, true, classLoader); } }; - } catch (final Throwable t2) { + } catch (final Throwable t) { // Fall through } } From abf53a2f20ccd62df087a1d800797a1ce6ffa0e0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Oct 2021 00:57:53 -0600 Subject: [PATCH 174/713] Don't hold a reference to classloader --- .../reflection/JVMDriverReflectionDriver.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 22d852589..61348e560 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -79,11 +79,11 @@ private static interface ClassFinder { // JDK 7 and 8 final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class); - final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { - return (Class) forName0_method.invoke(null, className, true, classLoader); + return (Class) forName0_method.invoke(null, className, true, + Thread.currentThread().getContextClassLoader()); } }; } catch (final Throwable t) { @@ -94,12 +94,11 @@ public Class findClass(final String className) throws Exception { // JDK 16 (and possibly earlier) final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class, Class.class); - final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { - return (Class) forName0_method.invoke(null, className, true, classLoader, - JVMDriverReflectionDriver.class); + return (Class) forName0_method.invoke(null, className, true, + Thread.currentThread().getContextClassLoader(), JVMDriverReflectionDriver.class); } }; } catch (final Throwable t) { @@ -111,11 +110,11 @@ public Class findClass(final String className) throws Exception { // IBM Semeru final Method forNameImpl_method = findMethod(Class.class, "forNameImpl", String.class, boolean.class, ClassLoader.class); - final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { - return (Class) forNameImpl_method.invoke(null, className, true, classLoader); + return (Class) forNameImpl_method.invoke(null, className, true, + Thread.currentThread().getContextClassLoader()); } }; } catch (final Throwable t) { From c93d79f49859d5ee1eeb249e7562302695fe0028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 02:02:23 +0000 Subject: [PATCH 175/713] Bump jvm-driver from 7.3.0 to 7.4.0 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 7.3.0 to 7.4.0. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-7.3.0...jvm-driver-7.4.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ae270bcd..b31749d11 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 7.3.0 + 7.4.0 true From 899a11e943bb4a743995ff3e34c3831dc52fea3a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 01:24:41 -0600 Subject: [PATCH 176/713] Only enable method parameter names during test compile (#577) --- pom.xml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 9ae270bcd..3016ce6bd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -282,7 +285,8 @@ - + org.codehaus.mojo.signature java17 @@ -361,8 +365,13 @@ - - + + @@ -393,9 +402,6 @@ 8 8 false - - -parameters - @@ -409,6 +415,9 @@ 8 8 + + -parameters + From c4c62662e9bc28f6b67a86ceaa8c0d19c3b5bd06 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 03:25:47 -0600 Subject: [PATCH 177/713] [maven-release-plugin] prepare release classgraph-4.8.126 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 3016ce6bd..e51e21986 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.126-SNAPSHOT + 4.8.126 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.118 + classgraph-4.8.126 @@ -285,8 +282,7 @@ - + org.codehaus.mojo.signature java17 @@ -365,13 +361,8 @@ - - + + From 89471d4b2de136ca62b51cb84578f91f575c5d9f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 03:26:03 -0600 Subject: [PATCH 178/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 53ab126f7..b0e855bfd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.126 + 4.8.127-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 27b7c4e870b7041f3a9ce8326e7fa0f6216903ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 03:34:09 -0600 Subject: [PATCH 179/713] Add -Xlint:all -Werror to javac args (#577) --- pom.xml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index b0e855bfd..ea90bfebd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -282,7 +285,8 @@ - + org.codehaus.mojo.signature java17 @@ -361,8 +365,13 @@ - - + + @@ -393,6 +402,10 @@ 8 8 false + + -Xlint:all + -Werror + From ec510dce09908de378984a1d5913d0a5087a57f9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 03:47:34 -0600 Subject: [PATCH 180/713] Remove getAnnotationInfoDirectOnly() (#559) --- src/main/java/io/github/classgraph/ClassInfo.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 57e3f5bcf..876ef9243 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1945,20 +1945,6 @@ public AnnotationInfoList getAnnotationInfo() { return AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); } - /** - * Get a list of the direct annotations on this class (i.e. not including meta-annotations or inherited - * annotations), or the empty list if none. - * - * @return A list of {@link AnnotationInfo} objects for the direct annotations on this class, or the empty list - * if none. - */ - public AnnotationInfoList getAnnotationInfoDirectOnly() { - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST : annotationInfo; - } - /** * Get a the non-{@link Repeatable} annotation on this class, or null if the class does not have the annotation. * (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) From d76f9c519f8beeb85fe27688c248ba5e998c5592 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 04:23:42 -0600 Subject: [PATCH 181/713] Fix deprecation warnings --- .../reflection/JVMDriverReflectionDriver.java | 10 ++++- .../reflection/StandardReflectionDriver.java | 37 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 61348e560..3571626de 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -64,7 +64,15 @@ private static interface ClassFinder { // throw new IllegalArgumentException("Could not load jvm-driver library"); // } final Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); - driver = driverClass.newInstance(); + for (final Constructor constructor : drv.getDeclaredConstructors(driverClass)) { + if (constructor.getParameterCount() == 0) { + driver = constructor.newInstance(); + break; + } + } + if (driver == null) { + throw new IllegalArgumentException("Could not instantiate jvm.DefaultDriver"); + } // Look up needed methods indexMethods(drv.enumerateDriverMethods(driverClass)); diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index 460329c7c..3fc497ba2 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -28,6 +28,9 @@ */ package nonapi.io.github.classgraph.reflection; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -40,6 +43,22 @@ * necessary). */ class StandardReflectionDriver extends ReflectionDriver { + private MethodHandle isAccessible; + private MethodHandle setAccessible; + + { + // Find deprecated methods, to remove compile-time warnings + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + isAccessible = lookup.findVirtual(AccessibleObject.class, "isAccessible", + MethodType.methodType(boolean.class)); + setAccessible = lookup.findVirtual(AccessibleObject.class, "setAccessible", + MethodType.methodType(void.class, boolean.class)); + } catch (final Exception e) { + // StandardReflectionDriver will stop working eventually, in some future version of Java + } + } + @Override Class findClass(final String className) throws Exception { return Class.forName(className); @@ -61,9 +80,21 @@ Field[] getDeclaredFields(final Class cls) throws Exception { return cls.getDeclaredFields(); } + private boolean isAccessible(final Object obj) { + try { + return (Boolean) isAccessible.invoke(); + } catch (final Throwable t) { + return false; + } + } + + private void setAccessible(final Object obj, final boolean flag) throws Throwable { + setAccessible.invoke(obj, flag); + } + @Override boolean makeAccessible(final AccessibleObject obj) { - if (!obj.isAccessible()) { + if (!isAccessible(obj)) { try { AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -74,12 +105,12 @@ public Void run() { }); } catch (final Throwable e) { try { - obj.setAccessible(true); + setAccessible(obj, true); } catch (final Throwable e2) { return false; } } - return obj.isAccessible(); + return isAccessible(obj); } return true; } From d9da7bd66bfcc8aded01f2fb37add8a727d6a8b0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 04:26:01 -0600 Subject: [PATCH 182/713] Fix Java 7 compat --- .../github/classgraph/reflection/JVMDriverReflectionDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 3571626de..a5b91e33d 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -65,7 +65,7 @@ private static interface ClassFinder { // } final Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); for (final Constructor constructor : drv.getDeclaredConstructors(driverClass)) { - if (constructor.getParameterCount() == 0) { + if (constructor.getParameterTypes().length == 0) { driver = constructor.newInstance(); break; } From 966f44022b6f9a6f41d7e8bc3d0c2628a018e1b2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 04:30:09 -0600 Subject: [PATCH 183/713] Fix isAccessible/setAccessible invocation --- .../classgraph/reflection/StandardReflectionDriver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index 3fc497ba2..5c49c0141 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -82,14 +82,14 @@ Field[] getDeclaredFields(final Class cls) throws Exception { private boolean isAccessible(final Object obj) { try { - return (Boolean) isAccessible.invoke(); + return (Boolean) isAccessible.invokeExact(obj); } catch (final Throwable t) { return false; } } private void setAccessible(final Object obj, final boolean flag) throws Throwable { - setAccessible.invoke(obj, flag); + setAccessible.invokeExact(obj, flag); } @Override From 9c6b5f09fbe899b54fec1322b2b5f5514b59cb9e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 04:44:06 -0600 Subject: [PATCH 184/713] Fix compiler warnings --- .../reflection/StandardReflectionDriver.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index 5c49c0141..eb45d3448 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -28,9 +28,6 @@ */ package nonapi.io.github.classgraph.reflection; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -43,19 +40,19 @@ * necessary). */ class StandardReflectionDriver extends ReflectionDriver { - private MethodHandle isAccessible; - private MethodHandle setAccessible; + private Method isAccessible; + private Method setAccessible; { - // Find deprecated methods, to remove compile-time warnings - final MethodHandles.Lookup lookup = MethodHandles.lookup(); + // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings + // TODO Switch to using MethodHandles until once this is fixed: + // https://github.com/mojohaus/animal-sniffer/issues/67 try { - isAccessible = lookup.findVirtual(AccessibleObject.class, "isAccessible", - MethodType.methodType(boolean.class)); - setAccessible = lookup.findVirtual(AccessibleObject.class, "setAccessible", - MethodType.methodType(void.class, boolean.class)); + isAccessible = AccessibleObject.class.getMethod("isAccessible"); + setAccessible = AccessibleObject.class.getMethod("setAccessible", boolean.class); } catch (final Exception e) { - // StandardReflectionDriver will stop working eventually, in some future version of Java + // StandardReflectionDriver will eventually stop working for non-public fields, + // in some future version of Java, when these methods are removed } } @@ -82,14 +79,14 @@ Field[] getDeclaredFields(final Class cls) throws Exception { private boolean isAccessible(final Object obj) { try { - return (Boolean) isAccessible.invokeExact(obj); + return (Boolean) isAccessible.invoke(obj); } catch (final Throwable t) { return false; } } private void setAccessible(final Object obj, final boolean flag) throws Throwable { - setAccessible.invokeExact(obj, flag); + setAccessible.invoke(obj, flag); } @Override From f3bbca1b8d2245ab48f1ef174a662ba2d3615567 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 04:45:56 -0600 Subject: [PATCH 185/713] Fix comment --- .../github/classgraph/reflection/StandardReflectionDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index eb45d3448..b607b0b8b 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -45,7 +45,7 @@ class StandardReflectionDriver extends ReflectionDriver { { // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings - // TODO Switch to using MethodHandles until once this is fixed: + // TODO Switch to using MethodHandles once this is fixed: // https://github.com/mojohaus/animal-sniffer/issues/67 try { isAccessible = AccessibleObject.class.getMethod("isAccessible"); From 2f8f98c874ca0bdbd75fb69f0836925544553cfc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 07:18:46 -0600 Subject: [PATCH 186/713] Use SecurityManager and AccessController via reflection (JDK17 compat) --- .../classgraph/classpath/CallStackReader.java | 77 +++--- .../concurrency/SimpleThreadFactory.java | 20 +- .../classgraph/json/ClassFieldCache.java | 4 +- .../io/github/classgraph/json/JSONUtils.java | 249 ++++++------------ .../reflection/JVMDriverReflectionDriver.java | 2 +- .../reflection/NarcissusReflectionDriver.java | 2 +- .../reflection/ReflectionDriver.java | 4 +- .../reflection/ReflectionUtils.java | 88 ++++++- .../reflection/StandardReflectionDriver.java | 116 ++++---- 9 files changed, 308 insertions(+), 254 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index 772f60b8a..7fb56dfd1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -28,14 +28,15 @@ */ package nonapi.io.github.classgraph.classpath; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; @@ -96,24 +97,7 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg // ------------------------------------------------------------------------------------------------------------- /** - * Using a SecurityManager gets around the fact that Oracle removed sun.reflect.Reflection.getCallerClass, see: - * - * https://www.infoq.com/news/2013/07/Oracle-Removes-getCallerClass - * - * http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html - */ - private static final class CallerResolver extends SecurityManager { - /* (non-Javadoc) - * @see java.lang.SecurityManager#getClassContext() - */ - @Override - protected Class[] getClassContext() { - return super.getClassContext(); - } - } - - /** - * Get the call stack via the SecurityManager API. + * Get the call stack via the SecurityManager.getClassContext() native method. * * @param log * the log @@ -121,12 +105,27 @@ protected Class[] getClassContext() { */ private static Class[] getCallStackViaSecurityManager(final LogNode log) { try { - return new CallerResolver().getClassContext(); - } catch (final SecurityException e) { + // Call method via reflection, since SecurityManager is deprecated in JDK 17. + final Class securityManagerClass = Class.forName("java.lang.SecurityManager"); + Object securityManager = null; + for (final Constructor constructor : securityManagerClass.getDeclaredConstructors()) { + if (constructor.getParameterTypes().length == 0) { + securityManager = constructor.newInstance(); + break; + } + } + if (securityManager != null) { + final Method getClassContext = securityManager.getClass().getDeclaredMethod("getClassContext"); + getClassContext.setAccessible(true); + return (Class[]) getClassContext.invoke(securityManager); + } else { + return null; + } + } catch (final Throwable t) { // Creating a SecurityManager can fail if the current SecurityManager does not allow // RuntimePermission("createSecurityManager") if (log != null) { - log.log("Exception while trying to obtain call stack via SecurityManager", e); + log.log("Exception while trying to obtain call stack via SecurityManager", t); } return null; } @@ -158,22 +157,30 @@ static Class[] getClassContext(final LogNode log) { || VersionFinder.JAVA_MAJOR_VERSION > 13) { // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html - callStack = AccessController.doPrivileged(new PrivilegedAction[]>() { - @Override - public Class[] run() { - return getCallStackViaStackWalker(); - } - }); + try { + callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + @Override + public Class[] call() throws Exception { + return getCallStackViaStackWalker(); + } + }); + } catch (final Throwable e) { + // Fall through + } } // For JRE 7 and 8, use SecurityManager to get call stack if (callStack == null || callStack.length == 0) { - callStack = AccessController.doPrivileged(new PrivilegedAction[]>() { - @Override - public Class[] run() { - return getCallStackViaSecurityManager(log); - } - }); + try { + callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + @Override + public Class[] call() throws Exception { + return getCallStackViaSecurityManager(log); + } + }); + } catch (final Throwable e) { + // Fall through + } } // As a fallback, use getStackTrace() to try to get the call stack diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java index 808f0619c..4b2cfea98 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.concurrency; +import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; /** @@ -36,7 +37,6 @@ * @author Johno Crawford (johno@sulake.com) */ public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { - /** The thread name prefix. */ private final String threadNamePrefix; @@ -68,10 +68,22 @@ public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { */ @Override public Thread newThread(final Runnable runnable) { - final SecurityManager s = System.getSecurityManager(); + // Call System.getSecurityManager().getThreadGroup() via reflection, since it is deprecated in JDK 17 + ThreadGroup securityManagerThreadGroup = null; + try { + final Method getSecurityManager = System.class.getDeclaredMethod("getSecurityManager"); + final Object securityManager = getSecurityManager.invoke(null); + if (securityManager != null) { + final Method getThreadGroup = securityManager.getClass().getDeclaredMethod("getThreadGroup"); + securityManagerThreadGroup = (ThreadGroup) getThreadGroup.invoke(securityManager); + } + } catch (final Throwable t) { + // Fall through + } final Thread thread = new Thread( - s != null ? s.getThreadGroup() : new ThreadGroup("ClassGraph-thread-group"), runnable, - threadNamePrefix + threadIdx.getAndIncrement()); + securityManagerThreadGroup != null ? securityManagerThreadGroup + : new ThreadGroup("ClassGraph-thread-group"), + runnable, threadNamePrefix + threadIdx.getAndIncrement()); thread.setDaemon(daemon); return thread; } diff --git a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java index 68d261fe9..0f22f0b17 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java @@ -204,7 +204,7 @@ Constructor getDefaultConstructorForConcreteTypeOf(final Class cls) { && (c != Object.class || cls == Object.class); c = c.getSuperclass()) { try { final Constructor defaultConstructor = c.getDeclaredConstructor(); - JSONUtils.isAccessibleOrMakeAccessible(defaultConstructor); + JSONUtils.makeAccessible(defaultConstructor); // Store found constructor in cache defaultConstructorForConcreteType.put(cls, defaultConstructor); return defaultConstructor; @@ -239,7 +239,7 @@ Constructor getConstructorWithSizeHintForConcreteTypeOf(final Class cls) { && (c != Object.class || cls == Object.class); c = c.getSuperclass()) { try { final Constructor constructorWithSizeHint = c.getDeclaredConstructor(Integer.TYPE); - JSONUtils.isAccessibleOrMakeAccessible(constructorWithSizeHint); + JSONUtils.makeAccessible(constructorWithSizeHint); // Store found constructor in cache constructorForConcreteTypeWithSizeHint.put(cls, constructorWithSizeHint); return constructorWithSizeHint; diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java index d3f473970..9133653d1 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -28,23 +28,100 @@ */ package nonapi.io.github.classgraph.json; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Collection; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.Callable; + +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** Utils for Java serialization and deserialization. */ public final class JSONUtils { + private static Method isAccessibleMethod; + private static Method setAccessibleMethod; + private static Method trySetAccessibleMethod; + + static { + // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings + // TODO Switch to using MethodHandles once this is fixed: + // https://github.com/mojohaus/animal-sniffer/issues/67 + try { + isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible"); + } catch (final Throwable t) { + // Ignore + } + try { + setAccessibleMethod = AccessibleObject.class.getDeclaredMethod("setAccessible", boolean.class); + } catch (final Throwable t) { + // Ignore + } + try { + trySetAccessibleMethod = AccessibleObject.class.getDeclaredMethod("trySetAccessible"); + } catch (final Throwable t) { + // Ignore + } + } + + private static boolean isAccessible(final AccessibleObject obj) { + if (isAccessibleMethod != null) { + // JDK 7/8: use isAccessible (deprecated in JDK 9+) + try { + if ((Boolean) isAccessibleMethod.invoke(obj)) { + return true; + } + } catch (final Throwable e) { + // Ignore + } + } + return false; + } + + private static boolean tryMakeAccessible(final AccessibleObject obj) { + if (setAccessibleMethod != null) { + try { + setAccessibleMethod.invoke(obj, true); + return true; + } catch (final Throwable e) { + // Ignore + } + } + if (trySetAccessibleMethod != null) { + try { + if ((Boolean) trySetAccessibleMethod.invoke(obj)) { + return true; + } + } catch (final Throwable e) { + // Ignore + } + } + return false; + } + + public static boolean makeAccessible(final AccessibleObject obj) { + // This reflection code is duplicated from StandardReflectionDriver, because calling + // ReflectionUtils.reflectionDriver.makeAccessible(obj) does not work when called from here + // (private fields can't be accessed from outside this package even after calling setAccessible(true)) + if (isAccessible(obj) || tryMakeAccessible(obj)) { + return true; + } + try { + return ReflectionUtils.doPrivileged(new Callable() { + @Override + public Boolean call() throws Exception { + return tryMakeAccessible(obj); + } + }); + } catch (final Throwable t) { + return false; + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** * JSON object key name for objects that are linked to from more than one object. Key name is only used if the * class that a JSON object was serialized from does not have its own id field annotated with {@link Id}. @@ -60,13 +137,6 @@ public final class JSONUtils { /** JSON character-to-string escaping replacements -- see http://www.json.org/ under "string". */ private static final String[] JSON_CHAR_REPLACEMENTS = new String[256]; - // private static MethodHandle canAccessMethodHandle = null; - // private static Method canAccessMethod = null; - private static MethodHandle isAccessibleMethodHandle = null; - private static Method isAccessibleMethod = null; - private static MethodHandle trySetAccessibleMethodHandle = null; - private static Method trySetAccessibleMethod = null; - static { for (int c = 0; c < 256; c++) { if (c == 32) { @@ -85,46 +155,6 @@ public final class JSONUtils { JSON_CHAR_REPLACEMENTS['\t'] = "\\t"; JSON_CHAR_REPLACEMENTS['\b'] = "\\b"; JSON_CHAR_REPLACEMENTS['\f'] = "\\f"; - - final Lookup lookup = MethodHandles.lookup(); - // try { - // // JDK 9+: use AccessibleObject::canAccess(instance) - // canAccessMethodHandle = lookup.findVirtual(AccessibleObject.class, "canAccess", - // MethodType.methodType(boolean.class, Object.class)); - // } catch (NoSuchMethodException | IllegalAccessException e) { - // // Ignore - // } - // try { - // canAccessMethod = AccessibleObject.class.getDeclaredMethod("canAccess"); - // } catch (NoSuchMethodException | SecurityException e1) { - // // Ignore - // } - try { - // JDK 7/8: use AccessibleObject::isAccessible() - isAccessibleMethodHandle = lookup.findVirtual(AccessibleObject.class, "isAccessible", - MethodType.methodType(boolean.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - - try { - isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible", Object.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - // JDK 9+: use AccessibleObject::trySetAccessible() rather than - // AccessibleObject::setAccessible(true) - trySetAccessibleMethodHandle = lookup.findVirtual(AccessibleObject.class, "trySetAccessible", - MethodType.methodType(boolean.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - try { - trySetAccessibleMethod = AccessibleObject.class.getDeclaredMethod("trySetAccessible"); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } } /** Lookup table for fast indenting. */ @@ -247,6 +277,7 @@ static void indent(final int depth, final int indentWidth, final StringBuilder b */ static Object getFieldValue(final Object containingObj, final Field field) throws IllegalArgumentException, IllegalAccessException { + // return ReflectionUtils.getFieldVal(true, containingObj, field.getName()); final Class fieldType = field.getType(); if (fieldType == Integer.TYPE) { return field.getInt(containingObj); @@ -358,114 +389,6 @@ static Class getRawType(final Type type) { } } - /** - * Return true if the field is accessible, or can be made accessible (and make it accessible if so). - * - * @param fieldOrConstructor - * the field or constructor - * @return true if accessible - */ - static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstructor) { - // Test if field or constructor is already accessible - final AtomicBoolean accessible = new AtomicBoolean(false); - // // TODO: this method needs to take an object instance reference before canAccess can be used - // if (canAccessMethodHandle != null) { - // // JDK 9+: use canAccess(instance) - // try { - // accessible.set((Boolean) canAccessMethodHandle.invokeExact(fieldOrConstructor, instance)); - // } catch (Throwable e) { - // // Ignore - // } - // } - // if (canAccessMethod != null) { - // accessible.set((Boolean) canAccessMethod.invoke(fieldOrConstructor, instance)); - // } - if (!accessible.get()) { - if (isAccessibleMethodHandle != null) { - // JDK 7/8: use isAccessible (deprecated in JDK 9+) - try { - // Have to use double casting and wrap in new Object[] due to Animal Sniffer bug: - // https://github.com/mojohaus/animal-sniffer/issues/67 - final Object invokeResult = isAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor }); - accessible.set((Boolean) invokeResult); - } catch (final Throwable e) { - // Ignore - } - } else if (isAccessibleMethod != null) { - // JDK 7/8: use isAccessible (deprecated in JDK 9+) - try { - accessible.set((Boolean) isAccessibleMethod.invoke(fieldOrConstructor)); - } catch (final Throwable e) { - // Ignore - } - } - } - - // Only set accessible if field or constructor is not yet accessible - if (!accessible.get()) { - if (trySetAccessibleMethodHandle != null) { - try { - // Have to use double casting and wrap in new Object[] due to Animal Sniffer bug: - // https://github.com/mojohaus/animal-sniffer/issues/67 - final Object invokeResult = trySetAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor }); - accessible.set((Boolean) invokeResult); - } catch (final Throwable e) { - // Ignore - } - } else if (trySetAccessibleMethod != null) { - try { - accessible.set((Boolean) trySetAccessibleMethod.invoke(fieldOrConstructor)); - } catch (final Throwable e) { - // Ignore - } - } - if (!accessible.get()) { - try { - fieldOrConstructor.setAccessible(true); - accessible.set(true); - } catch (final Throwable t) { - // Ignore - } - } - if (!accessible.get()) { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - if (trySetAccessibleMethodHandle != null) { - try { - // Have to use double casting and wrap in new Object[] due to Animal Sniffer bug: - // https://github.com/mojohaus/animal-sniffer/issues/67 - final Object invokeResult = trySetAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor }); - accessible.set((Boolean) invokeResult); - } catch (final Throwable e) { - // Ignore - } - } else if (trySetAccessibleMethod != null) { - try { - accessible.set((Boolean) trySetAccessibleMethod.invoke(fieldOrConstructor)); - } catch (final Throwable e) { - // Ignore - } - } - if (!accessible.get()) { - try { - fieldOrConstructor.setAccessible(true); - accessible.set(true); - } catch (final Throwable t) { - // Ignore - } - } - return null; - } - }); - } - } - return accessible.get(); - } - /** * Check if a field is serializable. Don't serialize transient, final, synthetic, or inaccessible fields. * @@ -483,7 +406,7 @@ static boolean fieldIsSerializable(final Field field, final boolean onlySerializ final int modifiers = field.getModifiers(); if ((!onlySerializePublicFields || Modifier.isPublic(modifiers)) && !Modifier.isTransient(modifiers) && !Modifier.isFinal(modifiers) && ((modifiers & 0x1000 /* synthetic */) == 0)) { - return JSONUtils.isAccessibleOrMakeAccessible(field); + return makeAccessible(field); } return false; } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index a5b91e33d..e006da500 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -143,7 +143,7 @@ public Class findClass(final String className) throws Exception { } @Override - boolean makeAccessible(final AccessibleObject accessibleObject) { + public boolean makeAccessible(final AccessibleObject accessibleObject) { try { setAccessibleMethod.invoke(driver, accessibleObject, true); } catch (final Throwable t) { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java index f23deeabe..a76e3f9a6 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -74,7 +74,7 @@ class NarcissusReflectionDriver extends ReflectionDriver { } @Override - boolean makeAccessible(final AccessibleObject accessibleObject) { + public boolean makeAccessible(final AccessibleObject accessibleObject) { return true; } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index adad5a53e..0cfe11765 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicReference; /** Reflection driver */ -abstract class ReflectionDriver { +public abstract class ReflectionDriver { private final Map> methodNameToMethods = new HashMap<>(); /** @@ -157,7 +157,7 @@ abstract class ReflectionDriver { * the field or method. * @return true if successful. */ - abstract boolean makeAccessible(final AccessibleObject accessibleObject); + public abstract boolean makeAccessible(final AccessibleObject accessibleObject); /** Iterator applied to each method of a class and its superclasses/interfaces. */ private static interface MethodIterator { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index ec250e3e0..f7553a6da 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -28,16 +28,34 @@ */ package nonapi.io.github.classgraph.reflection; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.concurrent.Callable; + import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassGraph.CircumventEncapsulationMethod; /** Reflection utility methods that can be used by ClassLoaderHandlers. */ public final class ReflectionUtils { /** The reflection driver to use. */ - private static ReflectionDriver reflectionDriver; + public static ReflectionDriver reflectionDriver; + + private static Class accessControllerClass; + private static Class privilegedActionClass; + private static Method accessControllerDoPrivileged; static { loadReflectionDriver(); + + try { + accessControllerClass = Class.forName("java.security.AccessController"); + privilegedActionClass = Class.forName("java.security.PrivilegedAction"); + accessControllerDoPrivileged = accessControllerClass.getMethod("doPrivileged", privilegedActionClass); + } catch (final Throwable t) { + // Ignore + } } /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ @@ -69,6 +87,43 @@ private ReflectionUtils() { // Cannot be constructed } + /** + * Get the value of the field in the class of the given object or any of its superclasses. If an exception is + * thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown + * wrapping the cause, otherwise this will return null. If passed a null object, returns null unless + * throwException is true, then throws IllegalArgumentException. + * + * @param throwException + * If true, throw an exception if the field value could not be read. + * @param obj + * The object. + * @param field + * The field. + * + * @return The field value. + * @throws IllegalArgumentException + * If the field value could not be read. + */ + public static Object getFieldVal(final boolean throwException, final Object obj, final Field field) + throws IllegalArgumentException { + if (obj == null || field == null) { + if (throwException) { + throw new NullPointerException(); + } else { + return null; + } + } + try { + return reflectionDriver.getField(obj, field); + } catch (final Throwable e) { + if (throwException) { + throw new IllegalArgumentException( + "Can't read field " + obj.getClass().getName() + "." + field.getName() + ": " + e); + } + } + return null; + } + /** * Get the value of the named field in the class of the given object or any of its superclasses. If an exception * is thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown @@ -314,4 +369,35 @@ public static Class classForNameOrNull(final String className) { } } + // ------------------------------------------------------------------------------------------------------------- + + private static class PrivilegedActionInvocationHandler implements InvocationHandler { + private final Callable callable; + + public PrivilegedActionInvocationHandler(final Callable callable) { + this.callable = callable; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + return callable.call(); + } + } + + /** + * Call a method in the AccessController.doPrivileged(PrivilegedAction) context, using reflection, if possible + * (AccessController is deprecated in JDK 17). + */ + @SuppressWarnings("unchecked") + public static T doPrivileged(final Callable callable) throws Throwable { + if (accessControllerDoPrivileged != null) { + final Object privilegedAction = Proxy.newProxyInstance(privilegedActionClass.getClassLoader(), + new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); + return (T) accessControllerDoPrivileged.invoke(null, privilegedAction); + } else { + // Fall back to invoking in a non-privileged context + return callable.call(); + } + } + } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index b607b0b8b..f28bafb03 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -32,28 +32,89 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; +import java.util.concurrent.Callable; /** * Standard reflection driver (uses {@link AccessibleObject#setAccessible(boolean)} to access non-public fields if * necessary). */ class StandardReflectionDriver extends ReflectionDriver { - private Method isAccessible; - private Method setAccessible; + private static Method isAccessibleMethod; + private static Method setAccessibleMethod; + private static Method trySetAccessibleMethod; - { + static { // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings // TODO Switch to using MethodHandles once this is fixed: // https://github.com/mojohaus/animal-sniffer/issues/67 try { - isAccessible = AccessibleObject.class.getMethod("isAccessible"); - setAccessible = AccessibleObject.class.getMethod("setAccessible", boolean.class); - } catch (final Exception e) { - // StandardReflectionDriver will eventually stop working for non-public fields, - // in some future version of Java, when these methods are removed + isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible"); + } catch (final Throwable t) { + // Ignore + } + try { + setAccessibleMethod = AccessibleObject.class.getDeclaredMethod("setAccessible", boolean.class); + } catch (final Throwable t) { + // Ignore + } + try { + trySetAccessibleMethod = AccessibleObject.class.getDeclaredMethod("trySetAccessible"); + } catch (final Throwable t) { + // Ignore + } + } + + private static boolean isAccessible(final AccessibleObject obj) { + if (isAccessibleMethod != null) { + // JDK 7/8: use isAccessible (deprecated in JDK 9+) + try { + if ((Boolean) isAccessibleMethod.invoke(obj)) { + return true; + } + } catch (final Throwable e) { + // Ignore + } + } + return false; + } + + private static boolean tryMakeAccessible(final AccessibleObject obj) { + if (setAccessibleMethod != null) { + try { + setAccessibleMethod.invoke(obj, true); + return true; + } catch (final Throwable e) { + // Ignore + } + } + if (trySetAccessibleMethod != null) { + try { + if ((Boolean) trySetAccessibleMethod.invoke(obj)) { + return true; + } + } catch (final Throwable e) { + // Ignore + } + } + return false; + } + + @Override + public boolean makeAccessible(final AccessibleObject obj) { + if (isAccessible(obj) || tryMakeAccessible(obj)) { + return true; + } + try { + return ReflectionUtils.doPrivileged(new Callable() { + @Override + public Boolean call() throws Exception { + return tryMakeAccessible(obj); + } + }); + } catch (final Throwable t) { + // Fall through } + return false; } @Override @@ -77,41 +138,6 @@ Field[] getDeclaredFields(final Class cls) throws Exception { return cls.getDeclaredFields(); } - private boolean isAccessible(final Object obj) { - try { - return (Boolean) isAccessible.invoke(obj); - } catch (final Throwable t) { - return false; - } - } - - private void setAccessible(final Object obj, final boolean flag) throws Throwable { - setAccessible.invoke(obj, flag); - } - - @Override - boolean makeAccessible(final AccessibleObject obj) { - if (!isAccessible(obj)) { - try { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - obj.setAccessible(true); - return null; - } - }); - } catch (final Throwable e) { - try { - setAccessible(obj, true); - } catch (final Throwable e2) { - return false; - } - } - return isAccessible(obj); - } - return true; - } - @Override Object getField(final Object object, final Field field) throws Exception { makeAccessible(field); From f660bb0b9d1228959b7f3c8e8738a576f1638b66 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 07:21:10 -0600 Subject: [PATCH 187/713] Enable testing on 18ea --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 284c7a503..a7775297c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15', '16', '17' ] + java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18ea' ] steps: - uses: actions/checkout@v2.3.4 - name: Set up JDK From 488b677f5cd75e0f091ce68c53053cf40e50680c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 07:42:10 -0600 Subject: [PATCH 188/713] More JDK 17 compat fixes --- .../io/github/classgraph/utils/FileUtils.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 7cf6a3cbc..aa8bf81e6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -39,10 +39,11 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; + +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** * File utilities. @@ -517,13 +518,17 @@ private static void lookupCleanMethodPrivileged() { } static { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Object run() { - lookupCleanMethodPrivileged(); - return null; - } - }); + try { + ReflectionUtils.doPrivileged(new Callable() { + @Override + public Void call() throws Exception { + lookupCleanMethodPrivileged(); + return null; + } + }); + } catch (Throwable e) { + // Ignore + } } /** @@ -644,12 +649,16 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff */ public static boolean closeDirectByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { if (byteBuffer != null && byteBuffer.isDirect()) { - return AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Boolean run() { - return closeDirectByteBufferPrivileged(byteBuffer, log); - } - }); + try { + return ReflectionUtils.doPrivileged(new Callable() { + @Override + public Boolean call() throws Exception { + return closeDirectByteBufferPrivileged(byteBuffer, log); + } + }); + } catch (Throwable t) { + return false; + } } else { // Nothing to unmap return false; From 495c40c5058a66dafdc0c4663e20298e175b9c83 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Oct 2021 07:46:33 -0600 Subject: [PATCH 189/713] Fix 18-ea version string --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7775297c..36b24e234 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18ea' ] + java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] steps: - uses: actions/checkout@v2.3.4 - name: Set up JDK From 928264d7da9de3955fe357f9d17382a5c3ba04a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 04:53:17 -0600 Subject: [PATCH 190/713] Fix reflective access warning (#579) --- .../reflection/JVMDriverReflectionDriver.java | 12 +- .../reflection/NarcissusReflectionDriver.java | 9 +- .../reflection/ReflectionDriver.java | 190 ++++++++++++++++-- .../reflection/ReflectionUtils.java | 14 +- .../reflection/StandardReflectionDriver.java | 50 ++--- .../io/github/classgraph/utils/FileUtils.java | 4 +- 6 files changed, 210 insertions(+), 69 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index e006da500..90782efd2 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -85,7 +85,7 @@ private static interface ClassFinder { setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); try { // JDK 7 and 8 - final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, + final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, boolean.class, ClassLoader.class); classFinder = new ClassFinder() { @Override @@ -100,8 +100,8 @@ public Class findClass(final String className) throws Exception { if (classFinder == null) { try { // JDK 16 (and possibly earlier) - final Method forName0_method = findMethod(Class.class, "forName0", String.class, boolean.class, - ClassLoader.class, Class.class); + final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, + boolean.class, ClassLoader.class, Class.class); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { @@ -116,7 +116,7 @@ public Class findClass(final String className) throws Exception { if (classFinder == null) { try { // IBM Semeru - final Method forNameImpl_method = findMethod(Class.class, "forNameImpl", String.class, + final Method forNameImpl_method = findStaticMethod(Class.class, "forNameImpl", String.class, boolean.class, ClassLoader.class); classFinder = new ClassFinder() { @Override @@ -132,7 +132,7 @@ public Class findClass(final String className) throws Exception { if (classFinder == null) { // Fallback if the above fails: just use Class.forName. // This won't find private non-exported classes in other modules. - final Method forName_method = findMethod(Class.class, "forName", String.class); + final Method forName_method = findStaticMethod(Class.class, "forName", String.class); classFinder = new ClassFinder() { @Override public Class findClass(final String className) throws Exception { @@ -143,7 +143,7 @@ public Class findClass(final String className) throws Exception { } @Override - public boolean makeAccessible(final AccessibleObject accessibleObject) { + public boolean makeAccessible(final Object instance, final AccessibleObject accessibleObject) { try { setAccessibleMethod.invoke(driver, accessibleObject, true); } catch (final Throwable t) { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java index a76e3f9a6..538667919 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -55,7 +55,7 @@ class NarcissusReflectionDriver extends ReflectionDriver { // Load Narcissus class via reflection, so that there is no runtime dependency final StandardReflectionDriver drv = new StandardReflectionDriver(); narcissusClass = drv.findClass("io.github.toolfactory.narcissus.Narcissus"); - if (!(Boolean) drv.getStaticField(drv.findField(narcissusClass, "libraryLoaded"))) { + if (!(Boolean) drv.getStaticField(drv.findStaticField(narcissusClass, "libraryLoaded"))) { throw new IllegalArgumentException("Could not load Narcissus native library"); } @@ -74,7 +74,12 @@ class NarcissusReflectionDriver extends ReflectionDriver { } @Override - public boolean makeAccessible(final AccessibleObject accessibleObject) { + public boolean isAccessible(final Object instance, final AccessibleObject obj) { + return true; + } + + @Override + public boolean makeAccessible(final Object instance, final AccessibleObject accessibleObject) { return true; } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 0cfe11765..018f585e8 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -45,6 +45,23 @@ /** Reflection driver */ public abstract class ReflectionDriver { private final Map> methodNameToMethods = new HashMap<>(); + private static Method isAccessibleMethod; + private static Method canAccessMethod; + + static { + // TODO Switch to using MethodHandles once this is fixed: + // https://github.com/mojohaus/animal-sniffer/issues/67 + try { + isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible"); + } catch (final Throwable t) { + // Ignore + } + try { + canAccessMethod = AccessibleObject.class.getDeclaredMethod("canAccess", Object.class); + } catch (final Throwable t) { + // Ignore + } + } /** * Find a class by name. @@ -150,14 +167,47 @@ public abstract class ReflectionDriver { */ abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; + /** + * Check whether a field or method is accessible. + * + * @param instance + * the object instance, or null if static. + * @param fieldOrMethod + * the field or method. + * + * @return true if accessible. + */ + public boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod) { + if (canAccessMethod != null) { + // JDK 9+: use canAccess + try { + return (Boolean) canAccessMethod.invoke(fieldOrMethod, instance); + } catch (final Throwable e) { + // Ignore + } + } + if (isAccessibleMethod != null) { + // JDK 7/8: use isAccessible (deprecated in JDK 9+) + try { + return (Boolean) isAccessibleMethod.invoke(fieldOrMethod); + } catch (final Throwable e) { + // Ignore + } + } + return false; + } + /** * Make a field or method accessible. * - * @param accessibleObject + * @param instance + * the object instance, or null if static. + * @param fieldOrMethod * the field or method. + * * @return true if successful. */ - public abstract boolean makeAccessible(final AccessibleObject accessibleObject); + public abstract boolean makeAccessible(final Object instance, final AccessibleObject fieldOrMethod); /** Iterator applied to each method of a class and its superclasses/interfaces. */ private static interface MethodIterator { @@ -250,7 +300,7 @@ void forAllMethods(final Class cls, final ReflectionDriver.MethodIterator met } /** - * Find a method by name and parameter types in the given class. + * Find a static method by name and parameter types in the given class. * * @param cls * the class @@ -262,27 +312,109 @@ void forAllMethods(final Class cls, final ReflectionDriver.MethodIterator met * @throws NoSuchMethodException * if the class does not contain a method of the given name and parameter types */ - Method findMethod(final Class cls, final String methodName, final Class... paramTypes) throws Exception { - final AtomicReference method = new AtomicReference<>(); + Method findStaticMethod(final Class cls, final String methodName, final Class... paramTypes) + throws Exception { + final AtomicReference methodFound = new AtomicReference<>(false); + final AtomicReference accessibleMethod = new AtomicReference<>(); + // First try to find an accessible version of the method, without calling setAccessible + // (this is needed for JPMS, since the implementing subclass of ModuleReference + // is not accessible, but its superclass is) forAllMethods(cls, new MethodIterator() { @Override public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes()) - // If method is not accessible, fall through and try superclass method of same - // name and paramTypes - && makeAccessible(m)) { - method.set(m); - return true; + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + methodFound.set(true); + if (isAccessible(null, m)) { + accessibleMethod.set(m); + // Stop iterating through methods + return true; + } + } + return false; + } + }); + if (accessibleMethod.get() != null) { + return accessibleMethod.get(); + } + if (methodFound.get()) { + // Method was found, but was not accessible -- try making method accessible + forAllMethods(cls, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + if (makeAccessible(null, m)) { + accessibleMethod.set(m); + // Stop iterating through methods + return true; + } + } + return false; + } + }); + if (accessibleMethod.get() != null) { + return accessibleMethod.get(); + } + } + throw new NoSuchMethodException(methodName); + } + + /** + * Find a method by name and parameter types in the class of the given object. + * + * @param obj + * the object + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name and parameter types + */ + Method findInstanceMethod(final Object obj, final String methodName, final Class... paramTypes) + throws Exception { + final AtomicReference methodFound = new AtomicReference<>(false); + final AtomicReference accessibleMethod = new AtomicReference<>(); + // First try to find an accessible version of the method, without calling setAccessible + // (this is needed for JPMS, since the implementing subclass of ModuleReference + // is not accessible, but its superclass is) + forAllMethods(obj.getClass(), new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + methodFound.set(true); + if (isAccessible(obj, m)) { + accessibleMethod.set(m); + // Stop iterating through methods + return true; + } } return false; } }); - final Method m = method.get(); - if (m != null) { - return m; - } else { - throw new NoSuchMethodException(methodName); + if (accessibleMethod.get() != null) { + return accessibleMethod.get(); + } + if (methodFound.get()) { + // Method was found, but was not accessible -- try making method accessible + forAllMethods(obj.getClass(), new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { + if (makeAccessible(obj, m)) { + accessibleMethod.set(m); + // Stop iterating through methods + return true; + } + } + return false; + } + }); + if (accessibleMethod.get() != null) { + return accessibleMethod.get(); + } } + throw new NoSuchMethodException(methodName); } /** @@ -316,10 +448,34 @@ public boolean foundMethod(final Method m) { * @throws NoSuchFieldException * if the class does not contain a field of the given name */ - Field findField(final Class cls, final String fieldName) throws Exception { + Field findStaticField(final Class cls, final String fieldName) throws Exception { for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Field field : getDeclaredFields(c)) { if (field.getName().equals(fieldName)) { + makeAccessible(null, field); + return field; + } + } + } + throw new NoSuchFieldException(fieldName); + } + + /** + * Find a field by name in the class of the given object. + * + * @param obj + * the object + * @param fieldName + * the field name. + * @return the {@link Field} + * @throws NoSuchFieldException + * if the class does not contain a field of the given name + */ + Field findInstanceField(final Object obj, final String fieldName) throws Exception { + for (Class c = obj.getClass(); c != null; c = c.getSuperclass()) { + for (final Field field : getDeclaredFields(c)) { + if (field.getName().equals(fieldName)) { + makeAccessible(obj, field); return field; } } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index f7553a6da..02791b5c5 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -151,7 +151,7 @@ public static Object getFieldVal(final boolean throwException, final Object obj, } } try { - return reflectionDriver.getField(obj, reflectionDriver.findField(obj.getClass(), fieldName)); + return reflectionDriver.getField(obj, reflectionDriver.findInstanceField(obj, fieldName)); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( @@ -188,7 +188,7 @@ public static Object getStaticFieldVal(final boolean throwException, final Class } } try { - return reflectionDriver.getStaticField(reflectionDriver.findField(cls, fieldName)); + return reflectionDriver.getStaticField(reflectionDriver.findStaticField(cls, fieldName)); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( @@ -225,7 +225,7 @@ public static Object invokeMethod(final boolean throwException, final Object obj } } try { - return reflectionDriver.invokeMethod(obj, reflectionDriver.findMethod(obj.getClass(), methodName)); + return reflectionDriver.invokeMethod(obj, reflectionDriver.findInstanceMethod(obj, methodName)); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); @@ -265,8 +265,8 @@ public static Object invokeMethod(final boolean throwException, final Object obj } } try { - return reflectionDriver.invokeMethod(obj, - reflectionDriver.findMethod(obj.getClass(), methodName, argType), param); + return reflectionDriver.invokeMethod(obj, reflectionDriver.findInstanceMethod(obj, methodName, argType), + param); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); @@ -302,7 +302,7 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas } } try { - return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName)); + return reflectionDriver.invokeStaticMethod(reflectionDriver.findStaticMethod(cls, methodName)); } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( @@ -343,7 +343,7 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas } } try { - return reflectionDriver.invokeStaticMethod(reflectionDriver.findMethod(cls, methodName, argType), + return reflectionDriver.invokeStaticMethod(reflectionDriver.findStaticMethod(cls, methodName, argType), param); } catch (final Throwable e) { if (throwException) { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index f28bafb03..24b5763e4 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -39,19 +39,13 @@ * necessary). */ class StandardReflectionDriver extends ReflectionDriver { - private static Method isAccessibleMethod; private static Method setAccessibleMethod; private static Method trySetAccessibleMethod; static { - // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings + // Find deprecated methods to remove compile-time warnings // TODO Switch to using MethodHandles once this is fixed: // https://github.com/mojohaus/animal-sniffer/issues/67 - try { - isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible"); - } catch (final Throwable t) { - // Ignore - } try { setAccessibleMethod = AccessibleObject.class.getDeclaredMethod("setAccessible", boolean.class); } catch (final Throwable t) { @@ -64,22 +58,17 @@ class StandardReflectionDriver extends ReflectionDriver { } } - private static boolean isAccessible(final AccessibleObject obj) { - if (isAccessibleMethod != null) { - // JDK 7/8: use isAccessible (deprecated in JDK 9+) + private static boolean tryMakeAccessible(final AccessibleObject obj) { + if (trySetAccessibleMethod != null) { + // JDK 9+ try { - if ((Boolean) isAccessibleMethod.invoke(obj)) { - return true; - } + return (Boolean) trySetAccessibleMethod.invoke(obj); } catch (final Throwable e) { // Ignore } } - return false; - } - - private static boolean tryMakeAccessible(final AccessibleObject obj) { if (setAccessibleMethod != null) { + // JDK 7/8 try { setAccessibleMethod.invoke(obj, true); return true; @@ -87,21 +76,12 @@ private static boolean tryMakeAccessible(final AccessibleObject obj) { // Ignore } } - if (trySetAccessibleMethod != null) { - try { - if ((Boolean) trySetAccessibleMethod.invoke(obj)) { - return true; - } - } catch (final Throwable e) { - // Ignore - } - } return false; } @Override - public boolean makeAccessible(final AccessibleObject obj) { - if (isAccessible(obj) || tryMakeAccessible(obj)) { + public boolean makeAccessible(final Object instance, final AccessibleObject obj) { + if (isAccessible(instance, obj)) { return true; } try { @@ -113,8 +93,8 @@ public Boolean call() throws Exception { }); } catch (final Throwable t) { // Fall through + return tryMakeAccessible(obj); } - return false; } @Override @@ -140,37 +120,37 @@ Field[] getDeclaredFields(final Class cls) throws Exception { @Override Object getField(final Object object, final Field field) throws Exception { - makeAccessible(field); + makeAccessible(object, field); return field.get(object); } @Override void setField(final Object object, final Field field, final Object value) throws Exception { - makeAccessible(field); + makeAccessible(object, field); field.set(object, value); } @Override Object getStaticField(final Field field) throws Exception { - makeAccessible(field); + makeAccessible(null, field); return field.get(null); } @Override void setStaticField(final Field field, final Object value) throws Exception { - makeAccessible(field); + makeAccessible(null, field); field.set(null, value); } @Override Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - makeAccessible(method); + makeAccessible(object, method); return method.invoke(object, args); } @Override Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - makeAccessible(method); + makeAccessible(null, method); return method.invoke(null, args); } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index aa8bf81e6..b248f2afc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -526,7 +526,7 @@ public Void call() throws Exception { return null; } }); - } catch (Throwable e) { + } catch (final Throwable e) { // Ignore } } @@ -656,7 +656,7 @@ public Boolean call() throws Exception { return closeDirectByteBufferPrivileged(byteBuffer, log); } }); - } catch (Throwable t) { + } catch (final Throwable t) { return false; } } else { From c1a0d4ea1fdbe7efdad2eb405f5cd7af57156aa6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 04:58:24 -0600 Subject: [PATCH 191/713] Change method names --- .../reflection/JVMDriverReflectionDriver.java | 16 +++++++------- .../reflection/NarcissusReflectionDriver.java | 22 +++++++++---------- .../reflection/ReflectionDriver.java | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 90782efd2..581c818a7 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -75,14 +75,14 @@ private static interface ClassFinder { } // Look up needed methods - indexMethods(drv.enumerateDriverMethods(driverClass)); - getDeclaredMethods = findDriverMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findDriverMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findDriverMethod("getDeclaredFields", Class.class); - getField = findDriverMethod("getFieldValue", Object.class, Field.class); - setField = findDriverMethod("setFieldValue", Object.class, Field.class, Object.class); - invokeMethod = findDriverMethod("invoke", Object.class, Method.class, Object[].class); - setAccessibleMethod = findDriverMethod("setAccessible", AccessibleObject.class, boolean.class); + indexDriverMethods(drv.enumerateDriverMethods(driverClass)); + getDeclaredMethods = findIndexedDriverMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findIndexedDriverMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findIndexedDriverMethod("getDeclaredFields", Class.class); + getField = findIndexedDriverMethod("getFieldValue", Object.class, Field.class); + setField = findIndexedDriverMethod("setFieldValue", Object.class, Field.class, Object.class); + invokeMethod = findIndexedDriverMethod("invoke", Object.class, Method.class, Object[].class); + setAccessibleMethod = findIndexedDriverMethod("setAccessible", AccessibleObject.class, boolean.class); try { // JDK 7 and 8 final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, boolean.class, diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java index 538667919..6c30c3650 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -60,17 +60,17 @@ class NarcissusReflectionDriver extends ReflectionDriver { } // Look up needed methods - indexMethods(drv.enumerateDriverMethods(narcissusClass)); - findClass = findDriverMethod("findClass", String.class); - getDeclaredMethods = findDriverMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findDriverMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findDriverMethod("getDeclaredFields", Class.class); - getField = findDriverMethod("getField", Object.class, Field.class); - setField = findDriverMethod("setField", Object.class, Field.class, Object.class); - getStaticField = findDriverMethod("getStaticField", Field.class); - setStaticField = findDriverMethod("setStaticField", Field.class, Object.class); - invokeMethod = findDriverMethod("invokeMethod", Object.class, Method.class, Object[].class); - invokeStaticMethod = findDriverMethod("invokeStaticMethod", Method.class, Object[].class); + indexDriverMethods(drv.enumerateDriverMethods(narcissusClass)); + findClass = findIndexedDriverMethod("findClass", String.class); + getDeclaredMethods = findIndexedDriverMethod("getDeclaredMethods", Class.class); + getDeclaredConstructors = findIndexedDriverMethod("getDeclaredConstructors", Class.class); + getDeclaredFields = findIndexedDriverMethod("getDeclaredFields", Class.class); + getField = findIndexedDriverMethod("getField", Object.class, Field.class); + setField = findIndexedDriverMethod("setField", Object.class, Field.class, Object.class); + getStaticField = findIndexedDriverMethod("getStaticField", Field.class); + setStaticField = findIndexedDriverMethod("setStaticField", Field.class, Object.class); + invokeMethod = findIndexedDriverMethod("invokeMethod", Object.class, Method.class, Object[].class); + invokeStaticMethod = findIndexedDriverMethod("invokeStaticMethod", Method.class, Object[].class); } @Override diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 018f585e8..e63e08d14 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -226,7 +226,7 @@ private static interface MethodIterator { * @throws NoSuchMethodException * If not found. */ - protected Method findDriverMethod(final String methodName, final Class... paramTypes) + protected Method findIndexedDriverMethod(final String methodName, final Class... paramTypes) throws NoSuchMethodException { final List methods = methodNameToMethods.get(methodName); if (methods != null) { @@ -245,7 +245,7 @@ protected Method findDriverMethod(final String methodName, final Class... par * @param methods * The methods to index. */ - protected void indexMethods(final List methods) { + protected void indexDriverMethods(final List methods) { // Index Narcissus methods by name for (final Method method : methods) { List methodsForName = methodNameToMethods.get(method.getName()); From 261ffb14a4fca5b29d25a993b17d7937e01583b3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 05:08:39 -0600 Subject: [PATCH 192/713] Don't iterate through interfaces when looking for static methods --- .../reflection/ReflectionDriver.java | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index e63e08d14..752967635 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -262,38 +262,47 @@ protected void indexDriverMethods(final List methods) { * * @param cls * the class + * @param includeInterfaceDefaultMethods + * iterate through default methods in interfaces + * @param methodIter + * The {@link MethodIter} to apply for each declared method */ - void forAllMethods(final Class cls, final ReflectionDriver.MethodIterator methodIter) throws Exception { + void forAllMethods(final Class cls, boolean includeInterfaceDefaultMethods, + final ReflectionDriver.MethodIterator methodIter) throws Exception { // Iterate from class to its superclasses, and find initial interfaces to start traversing from final Set> visited = new HashSet<>(); - final LinkedList> interfaceQueue = new LinkedList<>(); + final LinkedList> interfaceQueue = includeInterfaceDefaultMethods ? new LinkedList<>() : null; for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Method m : getDeclaredMethods(c)) { if (methodIter.foundMethod(m)) { return; } } - // Find interfaces and superinterfaces implemented by this class or its superclasses - if (c.isInterface() && visited.add(c)) { - interfaceQueue.add(c); - } - for (final Class iface : c.getInterfaces()) { - if (visited.add(iface)) { - interfaceQueue.add(iface); + if (interfaceQueue != null) { + // Find interfaces and superinterfaces implemented by this class or its superclasses + if (c.isInterface() && visited.add(c)) { + interfaceQueue.add(c); + } + for (final Class iface : c.getInterfaces()) { + if (visited.add(iface)) { + interfaceQueue.add(iface); + } } } } - // Traverse through interfaces looking for default methods - while (!interfaceQueue.isEmpty()) { - final Class iface = interfaceQueue.remove(); - for (final Method m : getDeclaredMethods(iface)) { - if (methodIter.foundMethod(m)) { - return; + if (interfaceQueue != null) { + // Traverse through interfaces looking for default methods + while (!interfaceQueue.isEmpty()) { + final Class iface = interfaceQueue.remove(); + for (final Method m : getDeclaredMethods(iface)) { + if (methodIter.foundMethod(m)) { + return; + } } - } - for (final Class superIface : iface.getInterfaces()) { - if (visited.add(superIface)) { - interfaceQueue.add(superIface); + for (final Class superIface : iface.getInterfaces()) { + if (visited.add(superIface)) { + interfaceQueue.add(superIface); + } } } } @@ -319,7 +328,7 @@ Method findStaticMethod(final Class cls, final String methodName, final Class // First try to find an accessible version of the method, without calling setAccessible // (this is needed for JPMS, since the implementing subclass of ModuleReference // is not accessible, but its superclass is) - forAllMethods(cls, new MethodIterator() { + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ false, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -338,7 +347,7 @@ public boolean foundMethod(final Method m) { } if (methodFound.get()) { // Method was found, but was not accessible -- try making method accessible - forAllMethods(cls, new MethodIterator() { + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ false, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -378,7 +387,7 @@ Method findInstanceMethod(final Object obj, final String methodName, final Class // First try to find an accessible version of the method, without calling setAccessible // (this is needed for JPMS, since the implementing subclass of ModuleReference // is not accessible, but its superclass is) - forAllMethods(obj.getClass(), new MethodIterator() { + forAllMethods(obj.getClass(), /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -397,7 +406,7 @@ public boolean foundMethod(final Method m) { } if (methodFound.get()) { // Method was found, but was not accessible -- try making method accessible - forAllMethods(obj.getClass(), new MethodIterator() { + forAllMethods(obj.getClass(), /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -427,7 +436,7 @@ public boolean foundMethod(final Method m) { */ List enumerateDriverMethods(final Class cls) throws Exception { final List methodOrder = new ArrayList<>(); - forAllMethods(cls, new MethodIterator() { + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { @Override public boolean foundMethod(final Method m) { methodOrder.add(m); From 8711220948fa3bab35a3328d56f1f573d599f28f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 05:30:10 -0600 Subject: [PATCH 193/713] Type inference fix --- .../io/github/classgraph/reflection/ReflectionDriver.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 752967635..4e705701b 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -267,11 +267,12 @@ protected void indexDriverMethods(final List methods) { * @param methodIter * The {@link MethodIter} to apply for each declared method */ - void forAllMethods(final Class cls, boolean includeInterfaceDefaultMethods, + void forAllMethods(final Class cls, final boolean includeInterfaceDefaultMethods, final ReflectionDriver.MethodIterator methodIter) throws Exception { // Iterate from class to its superclasses, and find initial interfaces to start traversing from final Set> visited = new HashSet<>(); - final LinkedList> interfaceQueue = includeInterfaceDefaultMethods ? new LinkedList<>() : null; + final LinkedList> interfaceQueue = includeInterfaceDefaultMethods ? new LinkedList>() + : null; for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Method m : getDeclaredMethods(c)) { if (methodIter.foundMethod(m)) { From 48a04aa6dd44987f39bd5026d9add59d5156240b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Oct 2021 02:01:13 +0000 Subject: [PATCH 194/713] Bump jvm-driver from 7.4.0 to 7.4.1 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 7.4.0 to 7.4.1. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-7.4.0...jvm-driver-7.4.1) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea90bfebd..d2f40472f 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ io.github.toolfactory jvm-driver - 7.4.0 + 7.4.1 true From 863ba695db26aadd112fec7169e06c6540b9fccb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 21:21:38 -0600 Subject: [PATCH 195/713] [maven-release-plugin] prepare release classgraph-4.8.127 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index ea90bfebd..6e6476483 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.127-SNAPSHOT + 4.8.127 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.126 + classgraph-4.8.127 @@ -285,8 +282,7 @@ - + org.codehaus.mojo.signature java17 @@ -365,13 +361,8 @@ - - + + From db679afcec6fc8a46712f8e509365cb69cf800ff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 13 Oct 2021 21:21:41 -0600 Subject: [PATCH 196/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6e6476483..f3657debc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.127 + 4.8.128-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.127 + classgraph-4.8.126 From f1e794667bac37707768dbb5e8b3a76aaab82c37 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 01:07:03 -0600 Subject: [PATCH 197/713] Refactoring --- .../reflection/ReflectionDriver.java | 238 +++++++++--------- 1 file changed, 116 insertions(+), 122 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 4e705701b..175320db0 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -44,7 +44,7 @@ /** Reflection driver */ public abstract class ReflectionDriver { - private final Map> methodNameToMethods = new HashMap<>(); + private final Map> driverMethodNameToMethods = new HashMap<>(); private static Method isAccessibleMethod; private static Method canAccessMethod; @@ -209,53 +209,14 @@ public boolean isAccessible(final Object instance, final AccessibleObject fieldO */ public abstract boolean makeAccessible(final Object instance, final AccessibleObject fieldOrMethod); + // ------------------------------------------------------------------------------------------------------------- + /** Iterator applied to each method of a class and its superclasses/interfaces. */ - private static interface MethodIterator { + protected static interface MethodIterator { /** @return true to stop iterating, or false to continue iterating */ boolean foundMethod(Method m) throws Exception; } - /** - * Find an indexed method. - * - * @param methodName - * The method name. - * @param paramTypes - * The parameter types. - * @return The method, if found. - * @throws NoSuchMethodException - * If not found. - */ - protected Method findIndexedDriverMethod(final String methodName, final Class... paramTypes) - throws NoSuchMethodException { - final List methods = methodNameToMethods.get(methodName); - if (methods != null) { - for (final Method method : methods) { - if (Arrays.equals(method.getParameterTypes(), paramTypes)) { - return method; - } - } - } - throw new NoSuchMethodException(methodName); - } - - /** - * Index a list of methods. - * - * @param methods - * The methods to index. - */ - protected void indexDriverMethods(final List methods) { - // Index Narcissus methods by name - for (final Method method : methods) { - List methodsForName = methodNameToMethods.get(method.getName()); - if (methodsForName == null) { - methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); - } - methodsForName.add(method); - } - } - /** * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to * collect all methods of the class and its superclasses, and any default methods defined in interfaces. @@ -267,8 +228,8 @@ protected void indexDriverMethods(final List methods) { * @param methodIter * The {@link MethodIter} to apply for each declared method */ - void forAllMethods(final Class cls, final boolean includeInterfaceDefaultMethods, - final ReflectionDriver.MethodIterator methodIter) throws Exception { + protected void forAllMethods(final Class cls, final boolean includeInterfaceDefaultMethods, + final MethodIterator methodIter) throws Exception { // Iterate from class to its superclasses, and find initial interfaces to start traversing from final Set> visited = new HashSet<>(); final LinkedList> interfaceQueue = includeInterfaceDefaultMethods ? new LinkedList>() @@ -309,70 +270,78 @@ void forAllMethods(final Class cls, final boolean includeInterfaceDefaultMeth } } + // ------------------------------------------------------------------------------------------------------------- + /** - * Find a static method by name and parameter types in the given class. - * - * @param cls - * the class + * Find an indexed method. + * * @param methodName - * the method name. + * The method name. * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} + * The parameter types. + * @return The method, if found. * @throws NoSuchMethodException - * if the class does not contain a method of the given name and parameter types + * If not found. */ - Method findStaticMethod(final Class cls, final String methodName, final Class... paramTypes) - throws Exception { - final AtomicReference methodFound = new AtomicReference<>(false); - final AtomicReference accessibleMethod = new AtomicReference<>(); - // First try to find an accessible version of the method, without calling setAccessible - // (this is needed for JPMS, since the implementing subclass of ModuleReference - // is not accessible, but its superclass is) - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ false, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { - methodFound.set(true); - if (isAccessible(null, m)) { - accessibleMethod.set(m); - // Stop iterating through methods - return true; - } + protected Method findIndexedDriverMethod(final String methodName, final Class... paramTypes) + throws NoSuchMethodException { + final List methods = driverMethodNameToMethods.get(methodName); + if (methods != null) { + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + return method; } - return false; } - }); - if (accessibleMethod.get() != null) { - return accessibleMethod.get(); } - if (methodFound.get()) { - // Method was found, but was not accessible -- try making method accessible - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ false, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { - if (makeAccessible(null, m)) { - accessibleMethod.set(m); - // Stop iterating through methods - return true; - } - } - return false; - } - }); - if (accessibleMethod.get() != null) { - return accessibleMethod.get(); + throw new NoSuchMethodException(methodName); + } + + /** + * Index a list of methods. + * + * @param methods + * The methods to index. + */ + protected void indexDriverMethods(final List methods) { + // Index Narcissus methods by name + for (final Method method : methods) { + List methodsForName = driverMethodNameToMethods.get(method.getName()); + if (methodsForName == null) { + driverMethodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); } + methodsForName.add(method); } - throw new NoSuchMethodException(methodName); } /** - * Find a method by name and parameter types in the class of the given object. + * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also iterates up + * through superclasses, to collect all methods of the class and its superclasses. + * + * @param cls + * the class + * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. + */ + protected List enumerateDriverMethods(final Class cls) throws Exception { + final List methodOrder = new ArrayList<>(); + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { + @Override + public boolean foundMethod(final Method m) { + methodOrder.add(m); + return false; + } + }); + return methodOrder; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Find a static method by name and parameter types in the given class. * + * @param cls + * the class * @param obj - * the object + * the object instance * @param methodName * the method name. * @param paramTypes @@ -381,14 +350,14 @@ public boolean foundMethod(final Method m) { * @throws NoSuchMethodException * if the class does not contain a method of the given name and parameter types */ - Method findInstanceMethod(final Object obj, final String methodName, final Class... paramTypes) - throws Exception { + private Method findMethod(final Class cls, final Object obj, final String methodName, + final Class... paramTypes) throws Exception { final AtomicReference methodFound = new AtomicReference<>(false); final AtomicReference accessibleMethod = new AtomicReference<>(); // First try to find an accessible version of the method, without calling setAccessible // (this is needed for JPMS, since the implementing subclass of ModuleReference // is not accessible, but its superclass is) - forAllMethods(obj.getClass(), /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ obj != null, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -407,7 +376,7 @@ public boolean foundMethod(final Method m) { } if (methodFound.get()) { // Method was found, but was not accessible -- try making method accessible - forAllMethods(obj.getClass(), /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { + forAllMethods(cls, /* includeInterfaceDefaultMethods = */ obj != null, new MethodIterator() { @Override public boolean foundMethod(final Method m) { if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { @@ -428,23 +397,39 @@ public boolean foundMethod(final Method m) { } /** - * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also iterates up - * through superclasses, to collect all methods of the class and its superclasses. + * Find a static method by name and parameter types in the given class. * * @param cls * the class - * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name and parameter types */ - List enumerateDriverMethods(final Class cls) throws Exception { - final List methodOrder = new ArrayList<>(); - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - methodOrder.add(m); - return false; - } - }); - return methodOrder; + Method findStaticMethod(final Class cls, final String methodName, final Class... paramTypes) + throws Exception { + return findMethod(cls, null, methodName, paramTypes); + } + + /** + * Find a method by name and parameter types in the class of the given object. + * + * @param obj + * the object + * @param methodName + * the method name. + * @param paramTypes + * the parameter types of the method. + * @return the {@link Method} + * @throws NoSuchMethodException + * if the class does not contain a method of the given name and parameter types + */ + Method findInstanceMethod(final Object obj, final String methodName, final Class... paramTypes) + throws Exception { + return findMethod(obj.getClass(), obj, methodName, paramTypes); } /** @@ -452,17 +437,19 @@ public boolean foundMethod(final Method m) { * * @param cls * the class + * @param obj + * the object instance * @param fieldName * the field name. * @return the {@link Field} * @throws NoSuchFieldException * if the class does not contain a field of the given name */ - Field findStaticField(final Class cls, final String fieldName) throws Exception { + private Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { for (Class c = cls; c != null; c = c.getSuperclass()) { for (final Field field : getDeclaredFields(c)) { if (field.getName().equals(fieldName)) { - makeAccessible(null, field); + makeAccessible(obj, field); return field; } } @@ -470,6 +457,21 @@ Field findStaticField(final Class cls, final String fieldName) throws Excepti throw new NoSuchFieldException(fieldName); } + /** + * Find a field by name in the given class. + * + * @param cls + * the class + * @param fieldName + * the field name. + * @return the {@link Field} + * @throws NoSuchFieldException + * if the class does not contain a field of the given name + */ + Field findStaticField(final Class cls, final String fieldName) throws Exception { + return findField(cls, null, fieldName); + } + /** * Find a field by name in the class of the given object. * @@ -482,14 +484,6 @@ Field findStaticField(final Class cls, final String fieldName) throws Excepti * if the class does not contain a field of the given name */ Field findInstanceField(final Object obj, final String fieldName) throws Exception { - for (Class c = obj.getClass(); c != null; c = c.getSuperclass()) { - for (final Field field : getDeclaredFields(c)) { - if (field.getName().equals(fieldName)) { - makeAccessible(obj, field); - return field; - } - } - } - throw new NoSuchFieldException(fieldName); + return findField(obj.getClass(), obj, fieldName); } } \ No newline at end of file From 39390890400ba69abdf547340ab76b2cc51dc778 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 03:24:35 -0600 Subject: [PATCH 198/713] Optimize reflection by caching fields and methods --- .../reflection/JVMDriverReflectionDriver.java | 16 +- .../reflection/NarcissusReflectionDriver.java | 23 +- .../reflection/ReflectionDriver.java | 423 ++++++++---------- 3 files changed, 204 insertions(+), 258 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java index 581c818a7..854102319 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java @@ -75,14 +75,14 @@ private static interface ClassFinder { } // Look up needed methods - indexDriverMethods(drv.enumerateDriverMethods(driverClass)); - getDeclaredMethods = findIndexedDriverMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findIndexedDriverMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findIndexedDriverMethod("getDeclaredFields", Class.class); - getField = findIndexedDriverMethod("getFieldValue", Object.class, Field.class); - setField = findIndexedDriverMethod("setFieldValue", Object.class, Field.class, Object.class); - invokeMethod = findIndexedDriverMethod("invoke", Object.class, Method.class, Object[].class); - setAccessibleMethod = findIndexedDriverMethod("setAccessible", AccessibleObject.class, boolean.class); + getDeclaredMethods = drv.findInstanceMethod(driver, "getDeclaredMethods", Class.class); + getDeclaredConstructors = drv.findInstanceMethod(driver, "getDeclaredConstructors", Class.class); + getDeclaredFields = drv.findInstanceMethod(driver, "getDeclaredFields", Class.class); + getField = drv.findInstanceMethod(driver, "getFieldValue", Object.class, Field.class); + setField = drv.findInstanceMethod(driver, "setFieldValue", Object.class, Field.class, Object.class); + invokeMethod = drv.findInstanceMethod(driver, "invoke", Object.class, Method.class, Object[].class); + setAccessibleMethod = drv.findInstanceMethod(driver, "setAccessible", AccessibleObject.class, + boolean.class); try { // JDK 7 and 8 final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, boolean.class, diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java index 6c30c3650..865cb0ed1 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/NarcissusReflectionDriver.java @@ -60,17 +60,18 @@ class NarcissusReflectionDriver extends ReflectionDriver { } // Look up needed methods - indexDriverMethods(drv.enumerateDriverMethods(narcissusClass)); - findClass = findIndexedDriverMethod("findClass", String.class); - getDeclaredMethods = findIndexedDriverMethod("getDeclaredMethods", Class.class); - getDeclaredConstructors = findIndexedDriverMethod("getDeclaredConstructors", Class.class); - getDeclaredFields = findIndexedDriverMethod("getDeclaredFields", Class.class); - getField = findIndexedDriverMethod("getField", Object.class, Field.class); - setField = findIndexedDriverMethod("setField", Object.class, Field.class, Object.class); - getStaticField = findIndexedDriverMethod("getStaticField", Field.class); - setStaticField = findIndexedDriverMethod("setStaticField", Field.class, Object.class); - invokeMethod = findIndexedDriverMethod("invokeMethod", Object.class, Method.class, Object[].class); - invokeStaticMethod = findIndexedDriverMethod("invokeStaticMethod", Method.class, Object[].class); + findClass = drv.findStaticMethod(narcissusClass, "findClass", String.class); + getDeclaredMethods = drv.findStaticMethod(narcissusClass, "getDeclaredMethods", Class.class); + getDeclaredConstructors = drv.findStaticMethod(narcissusClass, "getDeclaredConstructors", Class.class); + getDeclaredFields = drv.findStaticMethod(narcissusClass, "getDeclaredFields", Class.class); + getField = drv.findStaticMethod(narcissusClass, "getField", Object.class, Field.class); + setField = drv.findStaticMethod(narcissusClass, "setField", Object.class, Field.class, Object.class); + getStaticField = drv.findStaticMethod(narcissusClass, "getStaticField", Field.class); + setStaticField = drv.findStaticMethod(narcissusClass, "setStaticField", Field.class, Object.class); + invokeMethod = drv.findStaticMethod(narcissusClass, "invokeMethod", Object.class, Method.class, + Object[].class); + invokeStaticMethod = drv.findStaticMethod(narcissusClass, "invokeStaticMethod", Method.class, + Object[].class); } @Override diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 175320db0..87fd833da 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -40,15 +40,26 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; + +import nonapi.io.github.classgraph.concurrency.SingletonMap; +import nonapi.io.github.classgraph.utils.LogNode; /** Reflection driver */ -public abstract class ReflectionDriver { - private final Map> driverMethodNameToMethods = new HashMap<>(); +abstract class ReflectionDriver { + private final SingletonMap, ClassMemberCache, Exception> classToFieldMethodCache // + = new SingletonMap, ClassMemberCache, Exception>() { + @Override + public ClassMemberCache newInstance(final Class cls, final LogNode log) + throws Exception, InterruptedException { + return new ClassMemberCache(cls); + } + }; + private static Method isAccessibleMethod; private static Method canAccessMethod; static { + // Find deprecated methods to remove compile-time warnings // TODO Switch to using MethodHandles once this is fixed: // https://github.com/mojohaus/animal-sniffer/issues/67 try { @@ -63,6 +74,70 @@ public abstract class ReflectionDriver { } } + /** Caches class members. */ + public class ClassMemberCache { + private final Map> methodNameToMethods = new HashMap<>(); + private final Map fieldNameToField = new HashMap<>(); + + private ClassMemberCache(final Class cls) throws Exception { + // Iterate from class to its superclasses, and find initial interfaces to start traversing from + final Set> visited = new HashSet<>(); + final LinkedList> interfaceQueue = new LinkedList>(); + for (Class c = cls; c != null; c = c.getSuperclass()) { + try { + // Cache any declared methods and fields + for (final Method m : getDeclaredMethods(c)) { + cacheMethod(m); + } + for (final Field f : getDeclaredFields(c)) { + cacheField(f); + } + // Find interfaces and superinterfaces implemented by this class or its superclasses + if (c.isInterface() && visited.add(c)) { + interfaceQueue.add(c); + } + for (final Class iface : c.getInterfaces()) { + if (visited.add(iface)) { + interfaceQueue.add(iface); + } + } + } catch (final Exception e) { + // Skip + } + } + // Traverse through interfaces looking for default methods + while (!interfaceQueue.isEmpty()) { + final Class iface = interfaceQueue.remove(); + try { + for (final Method m : getDeclaredMethods(iface)) { + cacheMethod(m); + } + } catch (final Exception e) { + // Skip + } + for (final Class superIface : iface.getInterfaces()) { + if (visited.add(superIface)) { + interfaceQueue.add(superIface); + } + } + } + } + + private void cacheMethod(final Method method) { + List methodsForName = methodNameToMethods.get(method.getName()); + if (methodsForName == null) { + methodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); + } + methodsForName.add(method); + } + + private void cacheField(final Field field) { + // Only put a field name to field mapping if it is absent, so that subclasses mask fields + // of the same name in superclasses + fieldNameToField.putIfAbsent(field.getName(), field); + } + } + /** * Find a class by name. * @@ -167,6 +242,18 @@ public abstract class ReflectionDriver { */ abstract Object invokeStaticMethod(final Method method, final Object... args) throws Exception; + /** + * Make a field or method accessible. + * + * @param instance + * the object instance, or null if static. + * @param fieldOrMethod + * the field or method. + * + * @return true if successful. + */ + abstract boolean makeAccessible(final Object instance, final AccessibleObject fieldOrMethod); + /** * Check whether a field or method is accessible. * @@ -177,7 +264,8 @@ public abstract class ReflectionDriver { * * @return true if accessible. */ - public boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod) { + boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod) { + // Overridden in Narcissus driver to just return true, since everything is accessible to JNI if (canAccessMethod != null) { // JDK 9+: use canAccess try { @@ -198,292 +286,149 @@ public boolean isAccessible(final Object instance, final AccessibleObject fieldO } /** - * Make a field or method accessible. + * Get the field of the class that has a given field name. * - * @param instance - * the object instance, or null if static. - * @param fieldOrMethod - * the field or method. - * - * @return true if successful. - */ - public abstract boolean makeAccessible(final Object instance, final AccessibleObject fieldOrMethod); - - // ------------------------------------------------------------------------------------------------------------- - - /** Iterator applied to each method of a class and its superclasses/interfaces. */ - protected static interface MethodIterator { - /** @return true to stop iterating, or false to continue iterating */ - boolean foundMethod(Method m) throws Exception; - } - - /** - * Iterate through all methods in the given class. Also iterates up through superclasses and interfaces, to - * collect all methods of the class and its superclasses, and any default methods defined in interfaces. - * * @param cls - * the class - * @param includeInterfaceDefaultMethods - * iterate through default methods in interfaces - * @param methodIter - * The {@link MethodIter} to apply for each declared method + * the class. + * @param obj + * the object instance, or null for a static field. + * @param fieldName + * The name of the field. + * @return The {@link Field} object for the requested field name, or null if no such field was found in the + * class. + * @throws Exception + * if the field could not be found */ - protected void forAllMethods(final Class cls, final boolean includeInterfaceDefaultMethods, - final MethodIterator methodIter) throws Exception { - // Iterate from class to its superclasses, and find initial interfaces to start traversing from - final Set> visited = new HashSet<>(); - final LinkedList> interfaceQueue = includeInterfaceDefaultMethods ? new LinkedList>() - : null; - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Method m : getDeclaredMethods(c)) { - if (methodIter.foundMethod(m)) { - return; - } - } - if (interfaceQueue != null) { - // Find interfaces and superinterfaces implemented by this class or its superclasses - if (c.isInterface() && visited.add(c)) { - interfaceQueue.add(c); - } - for (final Class iface : c.getInterfaces()) { - if (visited.add(iface)) { - interfaceQueue.add(iface); - } - } - } - } - if (interfaceQueue != null) { - // Traverse through interfaces looking for default methods - while (!interfaceQueue.isEmpty()) { - final Class iface = interfaceQueue.remove(); - for (final Method m : getDeclaredMethods(iface)) { - if (methodIter.foundMethod(m)) { - return; - } - } - for (final Class superIface : iface.getInterfaces()) { - if (visited.add(superIface)) { - interfaceQueue.add(superIface); - } - } + private Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { + final Field field = classToFieldMethodCache.get(cls, /* log = */ null).fieldNameToField.get(fieldName); + if (field != null) { + if (!isAccessible(obj, field)) { + // If field was found but is not accessible, try making it accessible and then returning it + // (may result in a reflective access warning on stderr) + makeAccessible(obj, field); } + return field; } + throw new NoSuchFieldException("Could not find field " + cls.getName() + "." + fieldName); } - // ------------------------------------------------------------------------------------------------------------- - /** - * Find an indexed method. + * Get the static field of the class that has a given field name. * - * @param methodName - * The method name. - * @param paramTypes - * The parameter types. - * @return The method, if found. - * @throws NoSuchMethodException - * If not found. + * @param cls + * the class. + * @param fieldName + * The name of the field. + * @return The {@link Field} object for the requested field name, or null if no such field was found in the + * class. + * @throws Exception + * if the field could not be found */ - protected Method findIndexedDriverMethod(final String methodName, final Class... paramTypes) - throws NoSuchMethodException { - final List methods = driverMethodNameToMethods.get(methodName); - if (methods != null) { - for (final Method method : methods) { - if (Arrays.equals(method.getParameterTypes(), paramTypes)) { - return method; - } - } - } - throw new NoSuchMethodException(methodName); + protected Field findStaticField(final Class cls, final String fieldName) throws Exception { + return findField(cls, null, fieldName); } /** - * Index a list of methods. + * Get the non-static field of the class that has a given field name. * - * @param methods - * The methods to index. + * @param obj + * the object instance, or null for a static field. + * @param fieldName + * The name of the field. + * @return The {@link Field} object for the requested field name, or null if no such field was found in the + * class. + * @throws Exception + * if the field could not be found */ - protected void indexDriverMethods(final List methods) { - // Index Narcissus methods by name - for (final Method method : methods) { - List methodsForName = driverMethodNameToMethods.get(method.getName()); - if (methodsForName == null) { - driverMethodNameToMethods.put(method.getName(), methodsForName = new ArrayList<>()); - } - methodsForName.add(method); + protected Field findInstanceField(final Object obj, final String fieldName) throws Exception { + if (obj == null) { + throw new IllegalArgumentException("obj cannot be null"); } + return findField(obj.getClass(), obj, fieldName); } /** - * Enumerate all methods in the given class, ignoring visibility and bypassing security checks. Also iterates up - * through superclasses, to collect all methods of the class and its superclasses. - * - * @param cls - * the class - * @return a list of {@link Method} objects representing all methods declared by the class or a superclass. - */ - protected List enumerateDriverMethods(final Class cls) throws Exception { - final List methodOrder = new ArrayList<>(); - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ true, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - methodOrder.add(m); - return false; - } - }); - return methodOrder; - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * Find a static method by name and parameter types in the given class. - * + * Get a method by name and parameter types. + * * @param cls - * the class + * the class. * @param obj - * the object instance + * the object instance, or null for a static method. * @param methodName - * the method name. + * The name of the method. * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} - * @throws NoSuchMethodException - * if the class does not contain a method of the given name and parameter types + * The types of the parameters of the method. For primitive-typed parameters, use e.g. Integer.TYPE. + * @return The {@link Method} object for the matching method, or null if no such method was found in the class. + * @throws Exception + * if the method could not be found. */ private Method findMethod(final Class cls, final Object obj, final String methodName, final Class... paramTypes) throws Exception { - final AtomicReference methodFound = new AtomicReference<>(false); - final AtomicReference accessibleMethod = new AtomicReference<>(); - // First try to find an accessible version of the method, without calling setAccessible - // (this is needed for JPMS, since the implementing subclass of ModuleReference - // is not accessible, but its superclass is) - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ obj != null, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { - methodFound.set(true); - if (isAccessible(obj, m)) { - accessibleMethod.set(m); - // Stop iterating through methods - return true; + final List methods = classToFieldMethodCache.get(cls, null).methodNameToMethods.get(methodName); + if (methods != null) { + // Return the first method that matches the signature that is already accessible + boolean found = false; + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + found = true; + if (isAccessible(obj, method)) { + return method; } } - return false; } - }); - if (accessibleMethod.get() != null) { - return accessibleMethod.get(); - } - if (methodFound.get()) { - // Method was found, but was not accessible -- try making method accessible - forAllMethods(cls, /* includeInterfaceDefaultMethods = */ obj != null, new MethodIterator() { - @Override - public boolean foundMethod(final Method m) { - if (m.getName().equals(methodName) && Arrays.equals(paramTypes, m.getParameterTypes())) { - if (makeAccessible(obj, m)) { - accessibleMethod.set(m); - // Stop iterating through methods - return true; + // If method was found but is not accessible, try making it accessible and then returning it + // (may result in a reflective access warning on stderr) + if (found) { + for (final Method method : methods) { + if (Arrays.equals(method.getParameterTypes(), paramTypes)) { + if (makeAccessible(obj, method)) { + return method; } } - return false; } - }); - if (accessibleMethod.get() != null) { - return accessibleMethod.get(); } + throw new NoSuchMethodException( + "Could not make method accessible: " + cls.getName() + "." + methodName); } - throw new NoSuchMethodException(methodName); + throw new NoSuchMethodException("Could not find method " + cls.getName() + "." + methodName); } /** - * Find a static method by name and parameter types in the given class. - * + * Get a static method by name and parameter types. + * * @param cls - * the class + * the class. * @param methodName - * the method name. + * The name of the method. * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} - * @throws NoSuchMethodException - * if the class does not contain a method of the given name and parameter types + * The types of the parameters of the method. For primitive-typed parameters, use e.g. Integer.TYPE. + * @return The {@link Method} object for the matching method, or null if no such method was found in the class. + * @throws Exception + * if the method could not be found. */ - Method findStaticMethod(final Class cls, final String methodName, final Class... paramTypes) + protected Method findStaticMethod(final Class cls, final String methodName, final Class... paramTypes) throws Exception { return findMethod(cls, null, methodName, paramTypes); } /** - * Find a method by name and parameter types in the class of the given object. - * + * Get a non-static method by name and parameter types. + * * @param obj - * the object + * the object instance, or null for a static method. * @param methodName - * the method name. + * The name of the method. * @param paramTypes - * the parameter types of the method. - * @return the {@link Method} - * @throws NoSuchMethodException - * if the class does not contain a method of the given name and parameter types + * The types of the parameters of the method. For primitive-typed parameters, use e.g. Integer.TYPE. + * @return The {@link Method} object for the matching method, or null if no such method was found in the class. + * @throws Exception + * if the method could not be found. */ - Method findInstanceMethod(final Object obj, final String methodName, final Class... paramTypes) + protected Method findInstanceMethod(final Object obj, final String methodName, final Class... paramTypes) throws Exception { - return findMethod(obj.getClass(), obj, methodName, paramTypes); - } - - /** - * Find a field by name in the given class. - * - * @param cls - * the class - * @param obj - * the object instance - * @param fieldName - * the field name. - * @return the {@link Field} - * @throws NoSuchFieldException - * if the class does not contain a field of the given name - */ - private Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { - for (Class c = cls; c != null; c = c.getSuperclass()) { - for (final Field field : getDeclaredFields(c)) { - if (field.getName().equals(fieldName)) { - makeAccessible(obj, field); - return field; - } - } + if (obj == null) { + throw new IllegalArgumentException("obj cannot be null"); } - throw new NoSuchFieldException(fieldName); - } - - /** - * Find a field by name in the given class. - * - * @param cls - * the class - * @param fieldName - * the field name. - * @return the {@link Field} - * @throws NoSuchFieldException - * if the class does not contain a field of the given name - */ - Field findStaticField(final Class cls, final String fieldName) throws Exception { - return findField(cls, null, fieldName); - } - - /** - * Find a field by name in the class of the given object. - * - * @param obj - * the object - * @param fieldName - * the field name. - * @return the {@link Field} - * @throws NoSuchFieldException - * if the class does not contain a field of the given name - */ - Field findInstanceField(final Object obj, final String fieldName) throws Exception { - return findField(obj.getClass(), obj, fieldName); + return findMethod(obj.getClass(), obj, methodName, paramTypes); } } \ No newline at end of file From 66a43f7fac2283326707be29c21a6f43d4a4e953 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 03:27:12 -0600 Subject: [PATCH 199/713] JDK 7 fix --- .../io/github/classgraph/reflection/ReflectionDriver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 87fd833da..5dc07be7d 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -134,7 +134,9 @@ private void cacheMethod(final Method method) { private void cacheField(final Field field) { // Only put a field name to field mapping if it is absent, so that subclasses mask fields // of the same name in superclasses - fieldNameToField.putIfAbsent(field.getName(), field); + if (!fieldNameToField.containsKey(field.getName())) { + fieldNameToField.put(field.getName(), field); + } } } From 3138247e5dacb6eab15780098d66cb87438a5263 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 03:31:33 -0600 Subject: [PATCH 200/713] Small fixes --- .../classgraph/reflection/ReflectionDriver.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 5dc07be7d..1ae2683af 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -46,7 +46,7 @@ /** Reflection driver */ abstract class ReflectionDriver { - private final SingletonMap, ClassMemberCache, Exception> classToFieldMethodCache // + private final SingletonMap, ClassMemberCache, Exception> classToClassMemberCache // = new SingletonMap, ClassMemberCache, Exception>() { @Override public ClassMemberCache newInstance(final Class cls, final LogNode log) @@ -259,6 +259,9 @@ private void cacheField(final Field field) { /** * Check whether a field or method is accessible. * + *

+ * N.B. this is overridden in Narcissus driver to just return true, since everything is accessible to JNI. + * * @param instance * the object instance, or null if static. * @param fieldOrMethod @@ -267,7 +270,6 @@ private void cacheField(final Field field) { * @return true if accessible. */ boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod) { - // Overridden in Narcissus driver to just return true, since everything is accessible to JNI if (canAccessMethod != null) { // JDK 9+: use canAccess try { @@ -302,7 +304,7 @@ boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod * if the field could not be found */ private Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { - final Field field = classToFieldMethodCache.get(cls, /* log = */ null).fieldNameToField.get(fieldName); + final Field field = classToClassMemberCache.get(cls, /* log = */ null).fieldNameToField.get(fieldName); if (field != null) { if (!isAccessible(obj, field)) { // If field was found but is not accessible, try making it accessible and then returning it @@ -366,11 +368,12 @@ protected Field findInstanceField(final Object obj, final String fieldName) thro */ private Method findMethod(final Class cls, final Object obj, final String methodName, final Class... paramTypes) throws Exception { - final List methods = classToFieldMethodCache.get(cls, null).methodNameToMethods.get(methodName); - if (methods != null) { + final List methodsForName = classToClassMemberCache.get(cls, null).methodNameToMethods + .get(methodName); + if (methodsForName != null) { // Return the first method that matches the signature that is already accessible boolean found = false; - for (final Method method : methods) { + for (final Method method : methodsForName) { if (Arrays.equals(method.getParameterTypes(), paramTypes)) { found = true; if (isAccessible(obj, method)) { @@ -381,7 +384,7 @@ private Method findMethod(final Class cls, final Object obj, final String met // If method was found but is not accessible, try making it accessible and then returning it // (may result in a reflective access warning on stderr) if (found) { - for (final Method method : methods) { + for (final Method method : methodsForName) { if (Arrays.equals(method.getParameterTypes(), paramTypes)) { if (makeAccessible(obj, method)) { return method; From 47ac5268d51518fc70f3c14e708da415012850a5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 03:34:40 -0600 Subject: [PATCH 201/713] [maven-release-plugin] prepare release classgraph-4.8.128 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index df62be947..1a1676403 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.128-SNAPSHOT + 4.8.128 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.126 + classgraph-4.8.128 From c3ceb5b1bf1cae97047134dc81e1d898600e38ec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Oct 2021 03:34:43 -0600 Subject: [PATCH 202/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1a1676403..1cd37774f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.128 + 4.8.129-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.128 + classgraph-4.8.126 From 581eae7745f0704a8b80a26d9333da5149e23345 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 02:01:18 +0000 Subject: [PATCH 203/713] Bump jvm-driver from 7.4.1 to 8.2.1 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 7.4.1 to 8.2.1. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-7.4.1...jvm-driver-8.2.1) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1cd37774f..aa92d564b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 7.4.1 + 8.2.1 true From e67ba7497546dd84e4f0bdede94eea5beb6b8a56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:00:56 +0000 Subject: [PATCH 204/713] Bump actions/checkout from 2.3.4 to 2.3.5 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.4 to 2.3.5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.3.4...v2.3.5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36b24e234..dcaadd352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v2.3.5 - name: Set up JDK uses: actions/setup-java@v2 with: From 457c71abf0432157dca9a11c2565b12474b8911f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 17 Oct 2021 23:18:50 -0600 Subject: [PATCH 205/713] Test preservation of annotation default vals across serialization (#584) --- .../json/AnnotationDefaultVals.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java diff --git a/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java b/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java new file mode 100644 index 000000000..b6412210b --- /dev/null +++ b/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java @@ -0,0 +1,58 @@ +package io.github.classgraph.json; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import nonapi.io.github.classgraph.json.JSONDeserializer; +import nonapi.io.github.classgraph.json.JSONSerializer; + +/** + */ +@SuppressWarnings("unused") +public class AnnotationDefaultVals { + @Retention(RetentionPolicy.RUNTIME) + @interface MyAnnotation { + String msg() default "hello"; + } + + @MyAnnotation + class MyClass { + } + + /** + * Test serialize then deserialize scan result. + */ + @Test + public void testSerializeThenDeserializeWithAnnotation() { + // Get URL base for overriding classpath (otherwise the JSON representation of the ScanResult won't be + // the same after the first and second deserialization, because overrideClasspath is set by the first + // serialization for consistency.) + final String classfileURL = getClass().getClassLoader() + .getResource(AnnotationDefaultVals.class.getName().replace('.', '/') + ".class").toString(); + final String classpathBase = classfileURL.substring(0, + classfileURL.length() - (AnnotationDefaultVals.class.getName().length() + 6)); + try (ScanResult scanResult = new ClassGraph().overrideClasspath(classpathBase) + .acceptPackagesNonRecursive(AnnotationDefaultVals.class.getPackage().getName()) + .ignoreClassVisibility().enableAllInfo().scan()) { + assertThat(scanResult.getClassInfo(MyClass.class.getName()).getAnnotationInfo().get(0) + .getDefaultParameterValues().get(0).getValue()).isEqualTo("hello"); + final int indent = 2; + final String scanResultJSON = scanResult.toJSON(indent); + final ScanResult scanResultDeserialized = ScanResult.fromJSON(scanResultJSON); + final String scanResultReserializedJSON = scanResultDeserialized.toJSON(indent); + assertThat(scanResultReserializedJSON).isEqualTo(scanResultJSON); + assertThat(scanResultDeserialized.getClassInfo(MyClass.class.getName()).getAnnotationInfo().get(0) + .getDefaultParameterValues().get(0).getValue()).isEqualTo("hello"); + } + } +} From 684cd0a7cb6a8109116057293208cc09691a8637 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Oct 2021 00:33:10 -0600 Subject: [PATCH 206/713] Create duplicate of Javadoc using modular paths (#583). --- pom.xml | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1cd37774f..a86a8025f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -282,7 +285,8 @@ - + org.codehaus.mojo.signature java17 @@ -361,8 +365,35 @@ - - + + + + + + + + + + + add-modular-javadoc + deploy + + run + + + + + From cb88a156097c5c97767904a67a2296565ff51a19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Oct 2021 02:01:24 +0000 Subject: [PATCH 207/713] Bump jvm-driver from 8.2.1 to 8.2.2 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.2.1 to 8.2.2. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.2.1...jvm-driver-8.2.2) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c2f470af2..d5696c72b 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ io.github.toolfactory jvm-driver - 8.2.1 + 8.2.2 true From 901b05a0ac0427abd36ef5ebdea87e5b54728e46 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 15:59:18 -0600 Subject: [PATCH 208/713] Add getResourcesMatchingWildcard(String) (#588) --- .../java/io/github/classgraph/ScanResult.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b92a95611..da62f57fe 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -56,6 +56,7 @@ import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; +import nonapi.io.github.classgraph.scanspec.AcceptReject; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -636,7 +637,8 @@ public ResourceList getResourcesWithExtension(final String extension) { } /** - * Get the list of all resources found in accepted packages that have a path matching the requested pattern. + * Get the list of all resources found in accepted packages that have a path matching the requested regexp + * pattern. See also {{@link #getResourcesMatchingWildcard(String)}. * * @param pattern * A pattern to match {@link Resource} paths with. @@ -661,6 +663,26 @@ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { } } + /** + * Get the list of all resources found in accepted packages that have a path matching the requested wildcard + * string. The wildcard string may contain globs (asterisk wildcards). No other character has a special meaning. + * + *

+ * The wildcard string is translated in a simplistic way into a regex. If you need more complex pattern + * matching, use a regex directly, via {@link #getResourcesMatchingPattern(Pattern)}. + * + * @param wildcardString + * A wildcard (glob) pattern to match {@link Resource} paths with. + * @return A list of all resources found in accepted packages that have a path matching the requested wildcard + * string. + */ + public ResourceList getResourcesMatchingWildcard(final String wildcardString) { + if (closed.get()) { + throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); + } + return getResourcesMatchingPattern(AcceptReject.globToPattern(wildcardString)); + } + // ------------------------------------------------------------------------------------------------------------- // Modules From 3ba47a8cbb0f9c24ed9e8fce9ad72b2db5bf5859 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 16:03:40 -0600 Subject: [PATCH 209/713] Fix comment --- src/main/java/io/github/classgraph/ScanResult.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index da62f57fe..43df463cb 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -637,7 +637,7 @@ public ResourceList getResourcesWithExtension(final String extension) { } /** - * Get the list of all resources found in accepted packages that have a path matching the requested regexp + * Get the list of all resources found in accepted packages that have a path matching the requested regex * pattern. See also {{@link #getResourcesMatchingWildcard(String)}. * * @param pattern From eea473e4c317de3941faea0a48505cd816cd8a48 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 19:30:42 -0600 Subject: [PATCH 210/713] Add '?' to glob matching --- .../java/nonapi/io/github/classgraph/scanspec/AcceptReject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index 21fa8a945..33a0c79d2 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -573,7 +573,7 @@ public static String classNameToClassfilePath(final String className) { * @return The Pattern created from the glob string. */ public static Pattern globToPattern(final String glob) { - return Pattern.compile("^" + glob.replace(".", "\\.").replace("*", ".*") + "$"); + return Pattern.compile("^" + glob.replace(".", "\\.").replace("*", ".*").replace('?', '.') + "$"); } /** From 9286c9388fdb8eb4eb38df8699f03fec6d3996f4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 19:40:27 -0600 Subject: [PATCH 211/713] Differentiate between `*` and `**` (#588) --- .../io/github/classgraph/scanspec/AcceptReject.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index 33a0c79d2..e387bceee 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -565,15 +565,17 @@ public static String classNameToClassfilePath(final String className) { } /** - * Convert a spec with a '*' glob character into a regular expression. Replaces "." with "\." and "*" with ".*", - * then compiles a regular expression. + * Convert a spec with a '*' glob character into a regular expression. Replaces "." with "\.", "**" with ".*", + * "*" with "[^/]*", and "?" with ".", then compiles a regular expression. * * @param glob * The glob string. * @return The Pattern created from the glob string. */ public static Pattern globToPattern(final String glob) { - return Pattern.compile("^" + glob.replace(".", "\\.").replace("*", ".*").replace('?', '.') + "$"); + return Pattern.compile( + "^" + glob.replace(".", "\\.").replace("*", "[^/]*").replace("[^/]*[^/]*", ".*").replace('?', '.') + + "$"); } /** From 6856f40360adcc3386f62825e40aa221a72e522e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 19:43:41 -0600 Subject: [PATCH 212/713] Update Javadoc (#588) --- src/main/java/io/github/classgraph/ScanResult.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 43df463cb..5a94cd6ac 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -665,7 +665,17 @@ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { /** * Get the list of all resources found in accepted packages that have a path matching the requested wildcard - * string. The wildcard string may contain globs (asterisk wildcards). No other character has a special meaning. + * string. + * + *

+ * The wildcard string may contain: + *

    + *
  • Single asterisks, to match zero or more of any character other than '/'
  • + *
  • Double asterisks, to match zero or more of any character
  • + *
  • Question marks, to match one character
  • + *
  • Any other regexp-style syntax, such as character sets (denoted by square brackets) -- the remainder of + * the expression is passed through to the Java regex parser, after escaping dot characters.
  • + *
* *

* The wildcard string is translated in a simplistic way into a regex. If you need more complex pattern From 11c4ad1184b0f8c259df7ffbb48b39d624875d2c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 20:33:34 -0600 Subject: [PATCH 213/713] Update line spacing --- .../io/github/classgraph/scanspec/AcceptReject.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index e387bceee..3c4ae076a 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -573,9 +573,12 @@ public static String classNameToClassfilePath(final String className) { * @return The Pattern created from the glob string. */ public static Pattern globToPattern(final String glob) { - return Pattern.compile( - "^" + glob.replace(".", "\\.").replace("*", "[^/]*").replace("[^/]*[^/]*", ".*").replace('?', '.') - + "$"); + return Pattern.compile("^" // + + glob.replace(".", "\\.") // + .replace("*", "[^/]*") // + .replace("[^/]*[^/]*", ".*") // + .replace('?', '.') // + + "$"); } /** From d0a46d170c05d31090d1428215dea714bc08fa16 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:29:53 -0600 Subject: [PATCH 214/713] [maven-release-plugin] prepare release classgraph-4.8.129 --- pom.xml | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index d5696c72b..364085ca7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.129-SNAPSHOT + 4.8.129 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -34,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.126 + classgraph-4.8.129 @@ -285,8 +282,7 @@ - + org.codehaus.mojo.signature java17 @@ -365,13 +361,8 @@ - - + + @@ -387,13 +378,8 @@ - - + + From d8bfc33c672bb1059450c4dee5e87824d0573a4d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:29:55 -0600 Subject: [PATCH 215/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 364085ca7..a6a155da2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.129 + 4.8.130-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.129 + classgraph-4.8.126 From 629dd36307d556a0519ba739ed6d0830942e06b4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:39:56 -0600 Subject: [PATCH 216/713] Organize imports --- .../io/github/classgraph/json/AnnotationDefaultVals.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java b/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java index b6412210b..5b9854bde 100644 --- a/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java +++ b/src/test/java/io/github/classgraph/json/AnnotationDefaultVals.java @@ -4,17 +4,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.json.JSONDeserializer; -import nonapi.io.github.classgraph.json.JSONSerializer; /** */ From a50e433563d1f869c4fb2c67039e28faf97d0f17 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:42:19 -0600 Subject: [PATCH 217/713] Don't change glob behavior for accept/reject criteria (#588) --- .../java/io/github/classgraph/ClassGraph.java | 2 +- .../java/io/github/classgraph/ScanResult.java | 2 +- .../classgraph/scanspec/AcceptReject.java | 29 +++++++++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 45d51653d..06bc6414f 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1116,7 +1116,7 @@ private void acceptOrRejectLibOrExtJars(final boolean accept, final String... ja } if (jarLeafName.contains("*")) { // Compare wildcarded pattern against all jars in lib and ext dirs - final Pattern pattern = AcceptReject.globToPattern(jarLeafName); + final Pattern pattern = AcceptReject.globToPattern(jarLeafName, /* simpleGlob = */ true); boolean found = false; for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { final String libOrExtJarLeafName = JarUtils.leafName(libOrExtJarPath); diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 5a94cd6ac..b31432558 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -690,7 +690,7 @@ public ResourceList getResourcesMatchingWildcard(final String wildcardString) { if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - return getResourcesMatchingPattern(AcceptReject.globToPattern(wildcardString)); + return getResourcesMatchingPattern(AcceptReject.globToPattern(wildcardString, /* simpleGlob = */ false)); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index 3c4ae076a..a1ae17afb 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -245,7 +245,7 @@ public void addToAccept(final String str) { this.acceptPatterns = new ArrayList<>(); } this.acceptGlobs.add(str); - this.acceptPatterns.add(globToPattern(str)); + this.acceptPatterns.add(globToPattern(str, /* simpleGlob = */ true)); } else { if (this.accept == null) { this.accept = new HashSet<>(); @@ -301,7 +301,7 @@ public void addToReject(final String str) { this.rejectPatterns = new ArrayList<>(); } this.rejectGlobs.add(str); - this.rejectPatterns.add(globToPattern(str)); + this.rejectPatterns.add(globToPattern(str, /* simpleGlob = */ true)); } else { if (this.reject == null) { this.reject = new HashSet<>(); @@ -565,19 +565,30 @@ public static String classNameToClassfilePath(final String className) { } /** - * Convert a spec with a '*' glob character into a regular expression. Replaces "." with "\.", "**" with ".*", - * "*" with "[^/]*", and "?" with ".", then compiles a regular expression. + * Convert a spec with a '*' glob character into a regular expression. * * @param glob * The glob string. + * @param simpleGlob + * if true, handles simple globs: "*" matches zero or more characters (replaces "." with "\\.", "*" + * with ".*", then compiles a regular expression). If false, handles filesystem-style globs: "**" + * matches zero or more characters, "*" matches zero or more characters other than "/", "?" matches + * one character (replaces "." with "\\.", "**" with ".*", "*" with "[^/]*", and "?" with ".", then + * compiles a regular expression). * @return The Pattern created from the glob string. */ - public static Pattern globToPattern(final String glob) { + public static Pattern globToPattern(final String glob, final boolean simpleGlob) { + // TODO: when API is next broken, make all glob behavior consistent between accept/reject criteria + // and resource filtering (i.e. enforce simpleGlob == false) return Pattern.compile("^" // - + glob.replace(".", "\\.") // - .replace("*", "[^/]*") // - .replace("[^/]*[^/]*", ".*") // - .replace('?', '.') // + + (simpleGlob // + ? glob.replace(".", "\\.") // + .replace("*", ".*") // + : glob.replace(".", "\\.") // + .replace("*", "[^/]*") // + .replace("[^/]*[^/]*", ".*") // + .replace('?', '.') // + ) // + "$"); } From fbb1428bc402231fb03bbadcac59654c928c9dbc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:43:33 -0600 Subject: [PATCH 218/713] bump version back down --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a6a155da2..a4123851a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.130-SNAPSHOT + 4.8.129-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.126 + classgraph-4.8.129-SNAPSHOT From cf59b8e904599ea6776fe9f489c64ff9c27460d8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:43:52 -0600 Subject: [PATCH 219/713] [maven-release-plugin] prepare release classgraph-4.8.129 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a4123851a..364085ca7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.129-SNAPSHOT + 4.8.129 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.129-SNAPSHOT + classgraph-4.8.129 From 0920507efae1aadaf5763f40b5933e8b3f434289 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:43:54 -0600 Subject: [PATCH 220/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 364085ca7..7adcba717 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.129 + 4.8.130-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.129 + classgraph-4.8.129-SNAPSHOT From fc0e3f6cd77a5e1e3575d431fb0c7681dc73bf6d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:53:46 -0600 Subject: [PATCH 221/713] Update comment --- .../nonapi/io/github/classgraph/scanspec/AcceptReject.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index a1ae17afb..f7b098b75 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -578,8 +578,10 @@ public static String classNameToClassfilePath(final String className) { * @return The Pattern created from the glob string. */ public static Pattern globToPattern(final String glob, final boolean simpleGlob) { - // TODO: when API is next broken, make all glob behavior consistent between accept/reject criteria - // and resource filtering (i.e. enforce simpleGlob == false) + // TODO: when API is next changed, make all glob behavior consistent between accept/reject criteria + // and resource filtering (i.e. enforce simpleGlob == false, at least for accept/reject criteria for + // paths, although packages/classes would need different handling because ** should work across + // packages of any depth, rather than paths of any number of segments) return Pattern.compile("^" // + (simpleGlob // ? glob.replace(".", "\\.") // From c118c03ae89adc8e23bf9f8c79692763b0ca322c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:56:41 -0600 Subject: [PATCH 222/713] Create Javadoc before jars are signed --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7adcba717..83b7dbda6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.130-SNAPSHOT + 4.8.129-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,6 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.129-SNAPSHOT @@ -371,8 +370,9 @@ + add-modular-javadoc - deploy + verify run From 3d984092674e80609fa0354610a209693bca42d8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:57:05 -0600 Subject: [PATCH 223/713] [maven-release-plugin] prepare release classgraph-4.8.129 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 83b7dbda6..13b071498 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.129-SNAPSHOT + 4.8.129 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,8 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - + classgraph-4.8.129 + https://github.com/classgraph/classgraph/issues From 7888cd00c6bbed60d59a7bd2cdb8429c578ac7ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 22:57:07 -0600 Subject: [PATCH 224/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 13b071498..7237ab925 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.129 + 4.8.130-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.129 + HEAD From 40b7e5ef8a186686fd71e9645a9d25ef3bb4deb7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Oct 2021 23:00:07 -0600 Subject: [PATCH 225/713] Update comment --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7237ab925..42411d36b 100644 --- a/pom.xml +++ b/pom.xml @@ -159,7 +159,8 @@ - + + org.apache.maven.plugins maven-enforcer-plugin From 0787ef313c80714540d620d40d2ad9261b154c91 Mon Sep 17 00:00:00 2001 From: Jeffrey Gao Date: Tue, 2 Nov 2021 13:26:52 -0700 Subject: [PATCH 226/713] wrong @param value in doc, fix LGTM alert --- .../io/github/classgraph/reflection/ReflectionDriver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 1ae2683af..723c943e1 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -215,8 +215,8 @@ private void cacheField(final Field field) { * * @param field * the static field - * @param the - * value to set + * @param value + * the value to set */ abstract void setStaticField(final Field field, Object value) throws Exception; @@ -436,4 +436,4 @@ protected Method findInstanceMethod(final Object obj, final String methodName, f } return findMethod(obj.getClass(), obj, methodName, paramTypes); } -} \ No newline at end of file +} From 1e4a5b4cd46e6f7b20e868d807a6f4b9fb26c7fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Nov 2021 03:01:22 +0000 Subject: [PATCH 227/713] Bump actions/checkout from 2.3.5 to 2.4.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.5 to 2.4.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.3.5...v2.4.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcaadd352..c3c34e619 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] steps: - - uses: actions/checkout@v2.3.5 + - uses: actions/checkout@v2.4.0 - name: Set up JDK uses: actions/setup-java@v2 with: From 26efce82520d3f21a1cc36fce046cda1687f4715 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 3 Nov 2021 18:31:11 -0600 Subject: [PATCH 228/713] Remove finally blocks (in prep for JEP 421) --- pom.xml | 2 +- .../classgraph/ClassGraphClassLoader.java | 26 ++--- .../java/io/github/classgraph/Classfile.java | 9 +- .../github/classgraph/ClasspathElement.java | 17 +++ .../classgraph/ClasspathElementModule.java | 7 ++ .../classgraph/ClasspathElementZip.java | 4 + .../java/io/github/classgraph/Resource.java | 8 +- .../io/github/classgraph/ResourceList.java | 36 ++---- .../java/io/github/classgraph/Scanner.java | 43 +++++-- .../SpringBootRestartClassLoaderHandler.java | 2 +- .../classgraph/concurrency/SingletonMap.java | 29 ++++- .../classgraph/concurrency/WorkQueue.java | 33 +++--- .../fastzipfilereader/NestedJarHandler.java | 103 ++++++++-------- .../io/github/classgraph/json/JSONParser.java | 70 ++++++----- .../EncapsulationCircumventionTest.java | 14 ++- .../classgraph/issues/issue305/Issue305.java | 110 +++++++++--------- .../classgraph/test/utils/LogNodeTest.java | 66 ++++++----- 17 files changed, 324 insertions(+), 255 deletions(-) diff --git a/pom.xml b/pom.xml index 42411d36b..261808802 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ io.github.toolfactory narcissus - 1.0.6 + 1.0.7 true diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 8f6440dd5..895d133b1 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -263,25 +263,19 @@ protected Class findClass(final String className) if (classfileResources != null) { for (final Resource resource : classfileResources) { // Iterate through resources (only loading of first resource in the list will be attempted) - try { - // Load the content of the resource, and define a class from it - try { - final ByteBuffer resourceByteBuffer = resource.read(); - // TODO: is there any need to try java.lang.invoke.MethodHandles.Lookup.defineClass - // via reflection (it's implemented in JDK 9), if the following fails? - // See: https://bugs.openjdk.java.net/browse/JDK-8202999 - return defineClass(className, resourceByteBuffer, (ProtectionDomain) null); - } finally { - resource.close(); - } + // Load the content of the resource, and define a class from it + try (Resource resourceToClose = resource) { + final ByteBuffer resourceByteBuffer = resource.read(); + // TODO: is there any need to try java.lang.invoke.MethodHandles.Lookup.defineClass + // via reflection (it's implemented in JDK 9), if the following fails? + // See: https://bugs.openjdk.java.net/browse/JDK-8202999 + return defineClass(className, resourceByteBuffer, (ProtectionDomain) null); } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } - } finally { - resource.close(); } } } @@ -349,7 +343,7 @@ public URL getResource(final String path) { } } - // Finally if the above attempts fail, try retrieving resource from ScanResult. + // If the above attempts fail, try retrieving resource from ScanResult. // This will throw an exception if ScanResult has already been closed (#399). final ResourceList resourceList = scanResult.getResourcesWithPath(path); if (resourceList == null || resourceList.isEmpty()) { @@ -386,7 +380,7 @@ public Enumeration getResources(final String path) throws IOException { } } - // Finally if the above attempts fail, try retrieving resource from ScanResult. + // If the above attempts fail, try retrieving resource from ScanResult. // This will throw an exception if ScanResult has already been closed (#399). final ResourceList resourceList = scanResult.getResourcesWithPath(path); if (resourceList == null || resourceList.isEmpty()) { @@ -436,7 +430,7 @@ public InputStream getResourceAsStream(final String path) { } } - // Finally if the above attempts fail, try opening resource from ScanResult. + // If the above attempts fail, try opening resource from ScanResult. // This will throw an exception if ScanResult has already been closed (#399). final ResourceList resourceList = scanResult.getResourcesWithPath(path); if (resourceList == null || resourceList.isEmpty()) { diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7fbd5ce8f..0024c41bb 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1920,9 +1920,9 @@ public void decorate(final ClassTypeSignature classTypeSignature) { this.stringInternMap = stringInternMap; this.scanSpec = scanSpec; - try { - // Open a BufferedSequentialReader for the classfile - reader = classfileResource.openClassfile(); + // Open a BufferedSequentialReader for the classfile + try (ClassfileReader classfileReader = classfileResource.openClassfile()) { + reader = classfileReader; // Check magic number if (reader.readInt() != 0xCAFEBABE) { @@ -1951,9 +1951,6 @@ public void decorate(final ClassTypeSignature classTypeSignature) { // Read class attributes readClassAttributes(); - } finally { - // Close BufferedSequentialReader - classfileResource.close(); reader = null; } diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 69cdd00d8..6afc519ca 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -335,6 +335,23 @@ protected LogNode log(final int classpathElementIdx, final String msg, final Log return log.log(String.format("%07d", classpathElementIdx), msg); } + /** + * Write entries to log in classpath / module path order. + * + * @param classpathElementIdx + * the classpath element idx + * @param msg + * the log message + * @param t + * The exception that was thrown + * @param log + * the log + * @return the new {@link LogNode} + */ + protected LogNode log(final int classpathElementIdx, final String msg, final Throwable t, final LogNode log) { + return log.log(String.format("%07d", classpathElementIdx), msg, t); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 3ee78980e..cac5afd3e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -42,6 +42,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.SingletonMap; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; @@ -117,6 +118,12 @@ void open(final WorkQueue workQueueIgnored, final LogNod } skipClasspathElement = true; return; + } catch (final NewInstanceException e) { + if (log != null) { + log(classpathElementIdx, "Skipping invalid module " + getModuleName(), e, log); + } + skipClasspathElement = true; + return; } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index f675a52e5..f527f5d2e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -50,6 +50,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry; @@ -163,6 +164,9 @@ void open(final WorkQueue workQueue, final LogNode log) } catch (final NullSingletonException e) { // Generally thrown on the second and subsequent attempt to call .get(), after the first failed throw new IOException("Could not get logical zipfile " + rawPath + " : " + e); + } catch (final NewInstanceException e) { + // newInstance() threw an exception + throw new IOException("Could not get logical zipfile " + rawPath, e); } logicalZipFile = logicalZipFileAndPackageRoot.getKey(); if (logicalZipFile == null) { diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 2400915f9..40b262df6 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -212,11 +212,9 @@ public ModuleRef getModuleRef() { * If an I/O exception occurred. */ public String getContentAsString() throws IOException { - try { - return new String(load(), StandardCharsets.UTF_8); - } finally { - close(); - } + final String content = new String(load(), StandardCharsets.UTF_8); + close(); + return content; } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index b551ea12e..4bf70a964 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -363,15 +363,13 @@ public interface ByteArrayConsumerThrowsIOException { @Deprecated public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { final byte[] resourceContent = resource.load(); byteArrayConsumer.accept(resource, resourceContent); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); } - } finally { - resource.close(); } } } @@ -403,13 +401,11 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { */ public void forEachByteArrayIgnoringIOException(final ByteArrayConsumer byteArrayConsumer) { for (final Resource resource : this) { - try { + try (Resource resourceToClose = resource) { final byte[] resourceContent = resource.load(); byteArrayConsumer.accept(resource, resourceContent); } catch (final IOException e) { // Ignore - } finally { - resource.close(); } } } @@ -427,11 +423,9 @@ public void forEachByteArrayIgnoringIOException(final ByteArrayConsumer byteArra public void forEachByteArrayThrowingIOException( final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { - try { + try (Resource resourceToClose = resource) { final byte[] resourceContent = resource.load(); byteArrayConsumerThrowsIOException.accept(resource, resourceContent); - } finally { - resource.close(); } } } @@ -492,14 +486,12 @@ public interface InputStreamConsumerThrowsIOException { public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { inputStreamConsumer.accept(resource, resource.open()); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); } - } finally { - resource.close(); } } } @@ -531,12 +523,10 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { */ public void forEachInputStreamIgnoringIOException(final InputStreamConsumer inputStreamConsumer) { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { inputStreamConsumer.accept(resource, resource.open()); } catch (final IOException e) { // Ignore - } finally { - resource.close(); } } } @@ -555,10 +545,8 @@ public void forEachInputStreamIgnoringIOException(final InputStreamConsumer inpu public void forEachInputStreamThrowingIOException( final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { inputStreamConsumerThrowsIOException.accept(resource, resource.open()); - } finally { - resource.close(); } } } @@ -617,15 +605,13 @@ public interface ByteBufferConsumerThrowsIOException { @Deprecated public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { final ByteBuffer byteBuffer = resource.read(); byteBufferConsumer.accept(resource, byteBuffer); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); } - } finally { - resource.close(); } } } @@ -657,13 +643,11 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { */ public void forEachByteBufferIgnoringIOException(final ByteBufferConsumer byteBufferConsumer) { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { final ByteBuffer byteBuffer = resource.read(); byteBufferConsumer.accept(resource, byteBuffer); } catch (final IOException e) { // Ignore - } finally { - resource.close(); } } } @@ -681,11 +665,9 @@ public void forEachByteBufferIgnoringIOException(final ByteBufferConsumer byteBu public void forEachByteBufferThrowingIOException( final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { - try { + try (final Resource resourceToClose = resource) { final ByteBuffer byteBuffer = resource.read(); byteBufferConsumerThrowsIOException.accept(resource, byteBuffer); - } finally { - resource.close(); } } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index bdc74ab75..371941359 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -68,6 +68,7 @@ import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.concurrency.WorkQueue.WorkUnitProcessor; @@ -543,6 +544,10 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } catch (final NullSingletonException e) { throw new IOException("Cannot get classpath element for canonical path " + canonicalPathNormalized + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException( + "Cannot get classpath element for canonical path " + canonicalPathNormalized, + e); } } else { // Otherwise path is already canonical, and this is the first time this path has @@ -585,6 +590,10 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, } catch (final NullSingletonException e) { throw new IOException("Cannot get classpath element for classpath entry " + workUnit.rawClasspathEntry + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException( + "Cannot get classpath element for classpath entry " + workUnit.rawClasspathEntry, + e); } // Only run open() once per ClasspathElement (it is possible for there to be @@ -744,24 +753,27 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, // Enqueue the classfile for linking scannedClassfiles.add(classfile); + if (subLog != null) { + subLog.addElapsedTime(); + } } catch (final SkipClassException e) { if (subLog != null) { subLog.log(workUnit.classfileResource.getPath(), "Skipping classfile: " + e.getMessage()); + subLog.addElapsedTime(); } } catch (final ClassfileFormatException e) { if (subLog != null) { subLog.log(workUnit.classfileResource.getPath(), "Invalid classfile: " + e.getMessage()); + subLog.addElapsedTime(); } } catch (final IOException e) { if (subLog != null) { subLog.log(workUnit.classfileResource.getPath(), "Could not read classfile: " + e); + subLog.addElapsedTime(); } } catch (final Exception e) { if (subLog != null) { subLog.log(workUnit.classfileResource.getPath(), "Could not read classfile", e); - } - } finally { - if (subLog != null) { subLog.addElapsedTime(); } } @@ -1166,9 +1178,11 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe if (scanResultProcessor != null) { try { scanResultProcessor.processScanResult(scanResult); - } finally { + } catch (final Exception e) { scanResult.close(); + throw new ExecutionException(e); } + scanResult.close(); } } catch (final Throwable e) { @@ -1191,6 +1205,11 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe interruptionChecker.interrupt(); if (failureHandler == null) { + if (removeTemporaryFilesAfterScan) { + // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, + // zipfiles and modules + nestedJarHandler.close(topLevelLog); + } // If there is no failure handler set, re-throw the exception throw e; } else { @@ -1208,19 +1227,23 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe final ExecutionException failureHandlerException = new ExecutionException( "Exception while calling failure handler", f); failureHandlerException.addSuppressed(e); + if (removeTemporaryFilesAfterScan) { + // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, + // zipfiles and modules + nestedJarHandler.close(topLevelLog); + } // Throw a new ExecutionException (although this will probably be ignored, // since any job with a FailureHandler was started with ExecutorService::execute // rather than ExecutorService::submit) throw failureHandlerException; } } + } - } finally { - if (removeTemporaryFilesAfterScan) { - // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, - // zipfiles and modules - nestedJarHandler.close(topLevelLog); - } + if (removeTemporaryFilesAfterScan) { + // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, + // zipfiles and modules + nestedJarHandler.close(topLevelLog); } return scanResult; } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java index ad5f92ebd..9687ff2b2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -75,7 +75,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // classloader order first classLoaderOrder.add(classLoader, log); - // Finally delegate to the parent of the RestartClassLoader + // Delegate to the parent of the RestartClassLoader classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java index 3294337b8..988ca11f0 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -74,6 +74,26 @@ public NullSingletonException(final K key) { } } + /** Thrown when {@link SingletonMap#newInstance(Object, LogNode)} throws an exception. */ + public static class NewInstanceException extends Exception { + /** serialVersionUID. */ + static final long serialVersionUID = 1L; + + /** + * Constructor. + * + * @param + * the key type + * @param key + * the key + * @param t + * the Throwable that was thrown + */ + public NewInstanceException(final K key, final Throwable t) { + super("newInstance threw an exception for key " + key, t); + } + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -166,8 +186,11 @@ V get() throws InterruptedException { * thread. * @throws NullSingletonException * if {@link #newInstance(Object, LogNode)} returned null. + * @throws NewInstanceException + * if {@link #newInstance(Object, LogNode)} threw an exception. */ - public V get(final K key, final LogNode log) throws E, InterruptedException, NullSingletonException { + public V get(final K key, final LogNode log) + throws E, InterruptedException, NullSingletonException, NewInstanceException { final SingletonHolder singletonHolder = map.get(key); @SuppressWarnings("null") V instance = null; @@ -188,13 +211,15 @@ public V get(final K key, final LogNode log) throws E, InterruptedException, Nul // Create a new instance instance = newInstance(key, log); - } finally { + } catch (final Throwable t) { // Initialize newSingletonHolder with the new instance. // Always need to call .set() even if an exception is thrown by newInstance() // or newInstance() returns null, since .set() calls initialized.countDown(). // Otherwise threads that call .get() may end up waiting forever. newSingletonHolder.set(instance); + throw new NewInstanceException(key, t); } + newSingletonHolder.set(instance); } } if (instance == null) { diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java b/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java index 29d9aa99e..60de7e1e6 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java @@ -229,39 +229,40 @@ private void sendPoisonPills() { private void runWorkLoop() throws InterruptedException, ExecutionException { // Get next work unit from queue for (;;) { - // Check for interruption - interruptionChecker.check(); + // Process the work unit + try { + // Check for interruption + interruptionChecker.check(); - // Get next work unit - final WorkUnitWrapper workUnitWrapper = workUnits.take(); + // Get next work unit + final WorkUnitWrapper workUnitWrapper = workUnits.take(); - if (workUnitWrapper.workUnit == null) { - // Received poison pill - break; - } + if (workUnitWrapper.workUnit == null) { + // Received poison pill + break; + } - // Process the work unit - try { // Process the work unit (may throw InterruptedException) workUnitProcessor.processWorkUnit(workUnitWrapper.workUnit, this, log); - } catch (InterruptedException | OutOfMemoryError e) { + } catch (InterruptedException | Error e) { // On InterruptedException or OutOfMemoryError, drain work queue, send poison pills, and re-throw workUnits.clear(); + numIncompleteWorkUnits.set(0); sendPoisonPills(); throw e; } catch (final RuntimeException e) { // On unchecked exception, drain work queue, send poison pills, and throw ExecutionException workUnits.clear(); + numIncompleteWorkUnits.set(0); sendPoisonPills(); throw new ExecutionException("Worker thread threw unchecked exception", e); - } finally { - if (numIncompleteWorkUnits.decrementAndGet() == 0) { - // No more work units -- send poison pills - sendPoisonPills(); - } + } + if (numIncompleteWorkUnits.decrementAndGet() == 0) { + // No more work units -- send poison pills + sendPoisonPills(); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 6abefd569..2a1597016 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.fastzipfilereader; import java.io.BufferedOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -191,6 +192,9 @@ public Entry newInstance(final String nestedJarPathRaw, // If getting PhysicalZipFile failed, re-wrap in IOException throw new IOException( "Could not get PhysicalZipFile for path " + nestedJarPath + " : " + e); + } catch (final NewInstanceException e) { + // If getting PhysicalZipFile failed, re-wrap in IOException + throw new IOException("Could not get PhysicalZipFile for path " + nestedJarPath, e); } catch (final SecurityException e) { // getCanonicalFile() failed (it may have also failed with IOException) throw new IOException( @@ -205,6 +209,8 @@ public Entry newInstance(final String nestedJarPathRaw, logicalZipFile = zipFileSliceToLogicalZipFileMap.get(topLevelSlice, log); } catch (final NullSingletonException e) { throw new IOException("Could not get toplevel slice " + topLevelSlice + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException("Could not get toplevel slice " + topLevelSlice, e); } // Return new logical zipfile with an empty package root @@ -228,6 +234,8 @@ public Entry newInstance(final String nestedJarPathRaw, .get(parentPath, log); } catch (final NullSingletonException e) { throw new IOException("Could not get parent logical zipfile " + parentPath + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException("Could not get parent logical zipfile " + parentPath, e); } // Only the last item in a '!'-delimited list can be a non-jar path, so the parent must @@ -312,6 +320,8 @@ public Entry newInstance(final String nestedJarPathRaw, } catch (final NullSingletonException e) { throw new IOException( "Could not get child zip entry slice " + childZipEntry + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException("Could not get child zip entry slice " + childZipEntry, e); } final LogNode zipSliceLog = log == null ? null @@ -326,6 +336,8 @@ public Entry newInstance(final String nestedJarPathRaw, } catch (final NullSingletonException e) { throw new IOException( "Could not get child logical zipfile " + childZipEntrySlice + " : " + e); + } catch (final NewInstanceException e) { + throw new IOException("Could not get child logical zipfile " + childZipEntrySlice, e); } // Return new logical zipfile with an empty package root @@ -453,12 +465,8 @@ public File makeTempFile(final String filePathBase, final boolean onlyUseLeafnam * If the temporary file is inaccessible. */ void removeTempFile(final File tempFile) throws IOException, SecurityException { - if (tempFiles.contains(tempFile)) { - try { - Files.delete(tempFile.toPath()); - } finally { - tempFiles.remove(tempFile); - } + if (tempFiles.remove(tempFile)) { + Files.delete(tempFile.toPath()); } else { throw new IOException("Not a temp file: " + tempFile); } @@ -539,12 +547,18 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } final URLConnection conn = url.openConnection(); - HttpURLConnection httpConn = null; - try { - long contentLengthHint = -1L; - if (conn instanceof HttpURLConnection) { - // Get content length from HTTP headers, if available - httpConn = (HttpURLConnection) url.openConnection(); + long contentLengthHint = -1L; + if (conn instanceof HttpURLConnection) { + // Get content length from HTTP headers, if available + final HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + try (Closeable httpConnCloser = new Closeable() { + @Override + public void close() { + if (httpConn != null) { + httpConn.disconnect(); + } + } + }) { httpConn.setRequestMethod("GET"); httpConn.setConnectTimeout(HTTP_TIMEOUT); if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) { @@ -555,41 +569,37 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } else { throw new IOException("Got response code " + httpConn.getResponseCode() + " for URL " + url); } - } else if (conn.getURL().getProtocol().equalsIgnoreCase("file")) { - // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that - // rewrites its URLs into "file:" URLs (see Issue400.java). - try { - // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile - // (this avoids going through an InputStream). Throws IOException if the file cannot be read. - final File file = new File(conn.getURL().toURI()); - return new PhysicalZipFile(file, this, log); - - } catch (final URISyntaxException e) { - // Fall through to open URL as InputStream below - } } + } else if (conn.getURL().getProtocol().equalsIgnoreCase("file")) { + // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that + // rewrites its URLs into "file:" URLs (see Issue400.java). + try { + // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile + // (this avoids going through an InputStream). Throws IOException if the file cannot be read. + final File file = new File(conn.getURL().toURI()); + return new PhysicalZipFile(file, this, log); - // Fetch content from URL - final LogNode subLog = log == null ? null : log.log("Downloading jar from URL " + jarURL); - try (InputStream inputStream = conn.getInputStream()) { - // Fetch the jar contents from the URL's InputStream. If it doesn't fit in RAM, spill over to disk. - final PhysicalZipFile physicalZipFile = new PhysicalZipFile(inputStream, contentLengthHint, jarURL, - this, subLog); - if (subLog != null) { - subLog.addElapsedTime(); - subLog.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " - + "the URL must be opened (possibly after an http(s) fetch) for every scan, " - + "and the same URL must also be separately opened by the ClassLoader *****"); - } - return physicalZipFile; - - } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + jarURL); + } catch (final URISyntaxException e) { + // Fall through to open URL as InputStream below } - } finally { - if (httpConn != null) { - httpConn.disconnect(); + } + + // Fetch content from URL + final LogNode subLog = log == null ? null : log.log("Downloading jar from URL " + jarURL); + try (InputStream inputStream = conn.getInputStream()) { + // Fetch the jar contents from the URL's InputStream. If it doesn't fit in RAM, spill over to disk. + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(inputStream, contentLengthHint, jarURL, + this, subLog); + if (subLog != null) { + subLog.addElapsedTime(); + subLog.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " + + "the URL must be opened (possibly after an http(s) fetch) for every scan, " + + "and the same URL must also be separately opened by the ClassLoader *****"); } + return physicalZipFile; + + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + jarURL); } } @@ -766,12 +776,11 @@ public void close() { if (!closed.getAndSet(true)) { try { rawInputStream.close(); - } catch (final IOException e) { + } catch (final Exception e) { // Ignore - } finally { - // Reset and recycle inflater instance - inflaterRecycler.recycle(recyclableInflater); } + // Reset and recycle inflater instance + inflaterRecycler.recycle(recyclableInflater); } } }; diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java b/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java index e7aab663a..ac398c548 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java @@ -403,45 +403,51 @@ private JSONObject parseJSONObject() throws ParseException { */ private Object parseJSON() throws ParseException { skipWhitespace(); - try { - final char c = peek(); - if (c == '{') { - // Parse a JSON object - return parseJSONObject(); + final char c = peek(); + if (c == '{') { + // Parse a JSON object + final JSONObject obj = parseJSONObject(); + skipWhitespace(); + return obj; - } else if (c == '[') { - // Parse a JSON array - return parseJSONArray(); + } else if (c == '[') { + // Parse a JSON array + final JSONArray arr = parseJSONArray(); + skipWhitespace(); + return arr; - } else if (c == '"') { - // Parse a JSON string or object reference - final CharSequence charSequence = parseString(); - if (charSequence == null) { - throw new ParseException(this, "Invalid string"); - } - return charSequence; + } else if (c == '"') { + // Parse a JSON string or object reference + final CharSequence charSequence = parseString(); + skipWhitespace(); + if (charSequence == null) { + throw new ParseException(this, "Invalid string"); + } + return charSequence; - } else if (peekMatches("true")) { - // Parse true value - advance(4); - return Boolean.TRUE; + } else if (peekMatches("true")) { + // Parse true value + advance(4); + skipWhitespace(); + return Boolean.TRUE; - } else if (peekMatches("false")) { - // Parse true value - advance(5); - return Boolean.FALSE; + } else if (peekMatches("false")) { + // Parse true value + advance(5); + skipWhitespace(); + return Boolean.FALSE; - } else if (peekMatches("null")) { - advance(4); - // Parse null value (in string representation) - return null; + } else if (peekMatches("null")) { + // Parse null value (in string representation) + advance(4); + skipWhitespace(); + return null; - } else { - // The only remaining option is that the value must be a number - return parseNumber(); - } - } finally { + } else { + // The only remaining option is that the value must be a number + final Number num = parseNumber(); skipWhitespace(); + return num; } } diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java index e7e2509ac..1285f4bca 100644 --- a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -13,6 +14,13 @@ * Encapsulation circumvention test. */ class EncapsulationCircumventionTest { + /** Reset encapsulation circumvention method after each test. */ + @AfterEach + void resetAfterEachTest() { + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; + ReflectionUtils.loadReflectionDriver(); + } + /** Test Narcissus. */ @Test void testNarcissus() { @@ -24,9 +32,6 @@ void testNarcissus() { .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() .scan()) { assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); - } finally { - ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; - ReflectionUtils.loadReflectionDriver(); } } @@ -41,9 +46,6 @@ void testJVMDriver() { .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() .scan()) { assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); - } finally { - ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; - ReflectionUtils.loadReflectionDriver(); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java index a44e0b3c2..353ca8175 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -10,6 +10,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -19,6 +20,16 @@ * Issue305. */ public class Issue305 { + private ConsoleHandler errPrintStreamHandler = null; + private final Logger rootLogger = Logger.getLogger(""); + + /** Reset encapsulation circumvention method after each test. */ + @AfterEach + void resetAfterTest() { + rootLogger.removeHandler(errPrintStreamHandler); + // Set to System.err + System.setErr(System.err); + } /** * Test that multi-line continuations in manifest file values are correctly assembled into a string. @@ -28,61 +39,52 @@ public class Issue305 { */ @Test public void issue305() throws Exception { - ConsoleHandler errPrintStreamHandler = null; - final Logger rootLogger = Logger.getLogger(""); - try { - // Record log output - final ByteArrayOutputStream err = new ByteArrayOutputStream(); - System.setErr(new PrintStream(err)); - final Logger log = Logger.getLogger(ClassGraph.class.getName()); - if (!log.isLoggable(Level.INFO)) { - throw new Exception("Could not create log"); - } - errPrintStreamHandler = new ConsoleHandler(); - errPrintStreamHandler.setLevel(Level.INFO); - rootLogger.addHandler(errPrintStreamHandler); - - try (ScanResult scanResult = new ClassGraph() - .overrideClassLoaders(new URLClassLoader(new URL[] { - Issue305.class.getClassLoader().getResource("class-path-manifest-entry.jar") })) - // This .verbose() is needed (stderr is captured) - .verbose().scan()) { - } - - final String systemErrMessages = new String(err.toByteArray()); - assertThat(systemErrMessages.indexOf("Found Class-Path entry in manifest file: " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/charsets.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/deploy.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/access-bridge-64.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/cldrdata.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/dnsns.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jaccess.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jfxrt.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/localedata.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/nashorn.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunec.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunjce_provider.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunmscapi.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunpkcs11.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/zipfs.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/javaws.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jce.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfr.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfxswt.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jsse.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/management-agent.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/plugin.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/resources.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/rt.jar " - + "file:/Z:/classgraphtest/target/classes/ " - + "file:/C:/Users/flame/.m2/repository/io/github/classgraph/classgraph/4.6.19/classgraph-4.6.19.jar " - + "file:/C:/Program%20Files/JetBrains/IntelliJ%20IDEA%20Community%20Edition%202018.2.1/lib/idea_rt.jar")) - .isGreaterThan(0); + // Record log output + final ByteArrayOutputStream err = new ByteArrayOutputStream(); + System.setErr(new PrintStream(err)); + final Logger log = Logger.getLogger(ClassGraph.class.getName()); + if (!log.isLoggable(Level.INFO)) { + throw new Exception("Could not create log"); + } + errPrintStreamHandler = new ConsoleHandler(); + errPrintStreamHandler.setLevel(Level.INFO); + rootLogger.addHandler(errPrintStreamHandler); - } finally { - rootLogger.removeHandler(errPrintStreamHandler); - // Set to System.err - System.setErr(System.err); + try (ScanResult scanResult = new ClassGraph() + .overrideClassLoaders(new URLClassLoader( + new URL[] { Issue305.class.getClassLoader().getResource("class-path-manifest-entry.jar") })) + // This .verbose() is needed (stderr is captured) + .verbose().scan()) { } + + final String systemErrMessages = new String(err.toByteArray()); + assertThat(systemErrMessages.indexOf("Found Class-Path entry in manifest file: " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/charsets.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/deploy.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/access-bridge-64.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/cldrdata.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/dnsns.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jaccess.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jfxrt.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/localedata.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/nashorn.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunec.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunjce_provider.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunmscapi.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunpkcs11.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/zipfs.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/javaws.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jce.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfr.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfxswt.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jsse.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/management-agent.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/plugin.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/resources.jar " + + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/rt.jar " + + "file:/Z:/classgraphtest/target/classes/ " + + "file:/C:/Users/flame/.m2/repository/io/github/classgraph/classgraph/4.6.19/classgraph-4.6.19.jar " + + "file:/C:/Program%20Files/JetBrains/IntelliJ%20IDEA%20Community%20Edition%202018.2.1/lib/idea_rt.jar")) + .isGreaterThan(0); } } diff --git a/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java b/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java index 94c1ee508..18beaa73e 100644 --- a/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java +++ b/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java @@ -8,6 +8,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -17,42 +18,43 @@ * LogNodeTest. */ public class LogNodeTest { + private ConsoleHandler errPrintStreamHandler = null; + private final Logger rootLogger = Logger.getLogger(""); + + /** Reset encapsulation circumvention method after each test. */ + @AfterEach + void resetAfterTest() { + rootLogger.removeHandler(errPrintStreamHandler); + // Set to System.err + System.setErr(System.err); + } + /** * Test log node logging to system err. */ @Test public void testLogNodeLoggingToSystemErr() { - ConsoleHandler errPrintStreamHandler = null; - final Logger rootLogger = Logger.getLogger(""); - try { - - // Set the System.err - final ByteArrayOutputStream err = new ByteArrayOutputStream(); - System.setErr(new PrintStream(err)); - - errPrintStreamHandler = new ConsoleHandler(); - errPrintStreamHandler.setLevel(Level.INFO); - rootLogger.addHandler(errPrintStreamHandler); - - final LogNode node = new LogNode(); - node.log("any logging message").log("child message").log("sub child message"); - node.log("another root"); - node.flush(); - - final Logger log = Logger.getLogger(ClassGraph.class.getName()); - if (log.isLoggable(Level.INFO)) { - final String systemErrMessages = new String(err.toByteArray()); - assertTrue(systemErrMessages.contains("any logging message")); - assertTrue(systemErrMessages.contains("-- child message")); - assertTrue(systemErrMessages.contains("---- sub child message")); - assertTrue(systemErrMessages.contains("another root")); - // System.out.println(systemErrMessages); - } // else logging will not take place - - } finally { - rootLogger.removeHandler(errPrintStreamHandler); - // Set to System.err - System.setErr(System.err); - } + // Set the System.err + final ByteArrayOutputStream err = new ByteArrayOutputStream(); + System.setErr(new PrintStream(err)); + + errPrintStreamHandler = new ConsoleHandler(); + errPrintStreamHandler.setLevel(Level.INFO); + rootLogger.addHandler(errPrintStreamHandler); + + final LogNode node = new LogNode(); + node.log("any logging message").log("child message").log("sub child message"); + node.log("another root"); + node.flush(); + + final Logger log = Logger.getLogger(ClassGraph.class.getName()); + if (log.isLoggable(Level.INFO)) { + final String systemErrMessages = new String(err.toByteArray()); + assertTrue(systemErrMessages.contains("any logging message")); + assertTrue(systemErrMessages.contains("-- child message")); + assertTrue(systemErrMessages.contains("---- sub child message")); + assertTrue(systemErrMessages.contains("another root")); + // System.out.println(systemErrMessages); + } // else logging will not take place } } From 38a495ecb0f2d70bdfd3317d26cc6582c8095989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Nov 2021 02:01:21 +0000 Subject: [PATCH 229/713] Bump jvm-driver from 8.2.2 to 8.2.3 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.2.2 to 8.2.3. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.2.2...jvm-driver-8.2.3) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 261808802..689364cd8 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.2.2 + 8.2.3 true From fbd60bfb674fd2c7c6d435a09807a51204a47eae Mon Sep 17 00:00:00 2001 From: Rob Oxspring Date: Fri, 5 Nov 2021 23:03:47 +0000 Subject: [PATCH 230/713] ClasspathFinder supports overrides and systemJars together --- .../classgraph/classpath/ClasspathFinder.java | 5 +- .../classpath/ClasspathFinderTest.java | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 964214e00..67606039b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -187,9 +187,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { + "context classloader"); } classLoaderOrderRespectingParentDelegation = contextClassLoaders; + } - } else if (scanSpec.overrideClassLoaders == null) { - // If system jars are not rejected, add JRE rt.jar to the beginning of the classpath + // If system jars are enabled, add JRE rt.jar to the beginning of the classpath + if (scanSpec.enableSystemJarsAndModules) { final String jreRtJar = SystemJarFinder.getJreRtJarPath(); // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java new file mode 100644 index 000000000..f6cf7054b --- /dev/null +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -0,0 +1,85 @@ +package nonapi.io.github.classgraph.classpath; + +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.LogNode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.io.TempDir; + +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +import static nonapi.io.github.classgraph.classpath.SystemJarFinder.getJreRtJarPath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ClasspathFinderTest { + + /** + * Test that {@link ScanSpec#enableSystemJarsAndModules}, {@link ScanSpec#ignoreParentClassLoaders}, and + * {@link ScanSpec#overrideClasspath} work in combination: + *

+ * Only the system jars and the override classpath should be found. + */ + @Test + @EnabledForJreRange(max = JRE.JAVA_8) + public void testOverrideClasspathAndEnableSystemJars(@TempDir Path tmpDir) throws Exception { + // Arrange + final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); + final ScanSpec scanSpec = new ScanSpec(); + scanSpec.enableSystemJarsAndModules = true; + scanSpec.ignoreParentClassLoaders = true; + scanSpec.overrideClasspath = Collections.singletonList(classesDir); + + // Act + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + + // Assert + final Set paths = new TreeSet<>(); + for (final String path : classpathFinder + .getClasspathOrder() + .getClasspathEntryUniqueResolvedPaths()) { + paths.add(Paths.get(path)); + } + assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); + assertTrue(paths.remove(Paths.get(getJreRtJarPath())), "Classpath should have contained system jars: " + paths); + assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); + } + + /** + * Test that {@link ScanSpec#enableSystemJarsAndModules}, {@link ScanSpec#ignoreParentClassLoaders}, and + * {@link ScanSpec#overrideClassLoaders} work in combination: + *

+ * Only the system jars and the override classloaders should be found. + */ + @Test + @EnabledForJreRange(max = JRE.JAVA_8) + public void testOverrideClassLoaderAndEnableSystemJars(@TempDir Path tmpDir) throws Exception { + // Arrange + final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); + final ScanSpec scanSpec = new ScanSpec(); + scanSpec.enableSystemJarsAndModules = true; + scanSpec.ignoreParentClassLoaders = true; + scanSpec.overrideClassLoaders(new URLClassLoader(new URL[]{classesDir.toUri().toURL()})); + + // Act + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + + // Assert + final Set paths = new TreeSet<>(); + for (final String path : classpathFinder + .getClasspathOrder() + .getClasspathEntryUniqueResolvedPaths()) { + paths.add(Paths.get(path)); + } + assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); + assertTrue(paths.remove(Paths.get(getJreRtJarPath())), "Classpath should have contained system jars: " + paths); + assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); + } +} From 656925cf950ba9ef6beb2aa4f8eaf86ba24188db Mon Sep 17 00:00:00 2001 From: Rob Oxspring Date: Sat, 6 Nov 2021 10:26:36 +0000 Subject: [PATCH 231/713] ClasspathFinder supports overrides and systemModules together --- .../classgraph/classpath/ClasspathFinder.java | 10 ++- .../classpath/ClasspathFinderTest.java | 69 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 67606039b..f3a986ad8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -121,7 +121,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // If so, need to enable module scanning. If not, disable module scanning, since only the provided // classloader(s) should be scanned. (#382) boolean scanModules; - if (scanSpec.overrideClasspath != null) { + if (scanSpec.enableSystemJarsAndModules) { + // Always scan modules if system modules are enabled + scanModules = true; + } else if (scanSpec.overrideClasspath != null) { // Don't scan modules if classpath is overridden scanModules = false; } else if (scanSpec.overrideClassLoaders != null) { @@ -281,13 +284,16 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); } + + // Only scan java.class.path if parent classloaders are not ignored, classloaders are not overridden, // and the classpath is not overridden, unless only module scanning was enabled, and an unnamed module // layer was encountered -- in this case, have to forcibly scan java.class.path, since the ModuleLayer // API doesn't allow for the opening of unnamed modules. if ((!scanSpec.ignoreParentClassLoaders && scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) - || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { + || (moduleFinder != null && moduleFinder.forceScanJavaClassPath()&& scanSpec.overrideClassLoaders == null + && scanSpec.overrideClasspath == null)) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java index f6cf7054b..cd77f29d4 100644 --- a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -17,6 +17,7 @@ import static nonapi.io.github.classgraph.classpath.SystemJarFinder.getJreRtJarPath; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class ClasspathFinderTest { @@ -82,4 +83,72 @@ public void testOverrideClassLoaderAndEnableSystemJars(@TempDir Path tmpDir) thr assertTrue(paths.remove(Paths.get(getJreRtJarPath())), "Classpath should have contained system jars: " + paths); assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); } + + /** + * Test that {@link ScanSpec#enableSystemJarsAndModules}, {@link ScanSpec#ignoreParentClassLoaders}, and + * {@link ScanSpec#overrideClasspath} work in combination: + *

+ * Only the system modules and the override classpath should be found. + */ + @Test + @EnabledForJreRange(min = JRE.JAVA_9) + public void testOverrideClasspathAndEnableSystemModules(@TempDir Path tmpDir) throws Exception { + // Arrange + final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); + final ScanSpec scanSpec = new ScanSpec(); + scanSpec.enableSystemJarsAndModules = true; + scanSpec.ignoreParentClassLoaders = true; + scanSpec.overrideClasspath = Collections.singletonList(classesDir); + + // Act + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); + + // Assert + assertNotNull(moduleFinder, "ModuleFinder should be non-null"); + assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); + + final Set paths = new TreeSet<>(); + for (final String path : classpathFinder + .getClasspathOrder() + .getClasspathEntryUniqueResolvedPaths()) { + paths.add(Paths.get(path)); + } + assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); + assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); + } + + /** + * Test that {@link ScanSpec#enableSystemJarsAndModules}, {@link ScanSpec#ignoreParentClassLoaders}, and + * {@link ScanSpec#overrideClassLoaders} work in combination: + *

+ * Only the system modules and the override classloaders should be found. + */ + @Test + @EnabledForJreRange(min = JRE.JAVA_9) + public void testOverrideClassLoaderAndEnableSystemModules(@TempDir Path tmpDir) throws Exception { + // Arrange + final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); + final ScanSpec scanSpec = new ScanSpec(); + scanSpec.enableSystemJarsAndModules = true; + scanSpec.ignoreParentClassLoaders = true; + scanSpec.overrideClassLoaders(new URLClassLoader(new URL[]{classesDir.toUri().toURL()})); + + // Act + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); + + // Assert + assertNotNull(moduleFinder, "ModuleFinder should be non-null"); + assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); + + final Set paths = new TreeSet<>(); + for (final String path : classpathFinder + .getClasspathOrder() + .getClasspathEntryUniqueResolvedPaths()) { + paths.add(Paths.get(path)); + } + assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); + assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); + } } From cb230dc6151390a0e70e1d166d85ae15755663f5 Mon Sep 17 00:00:00 2001 From: Rob Oxspring Date: Sat, 6 Nov 2021 10:50:08 +0000 Subject: [PATCH 232/713] Simplify java.class.path scan condition --- .../classgraph/classpath/ClasspathFinder.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index f3a986ad8..b07b22973 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -284,16 +284,15 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); } + // Never scan java.class.path if classloaders or classpath are overridden + if (scanSpec.overrideClassLoaders != null || scanSpec.overrideClasspath != null) { + return; + } - - // Only scan java.class.path if parent classloaders are not ignored, classloaders are not overridden, - // and the classpath is not overridden, unless only module scanning was enabled, and an unnamed module + // Scan java.class.path if parent classloaders are not ignored, or if module scanning was enabled and an unnamed module // layer was encountered -- in this case, have to forcibly scan java.class.path, since the ModuleLayer // API doesn't allow for the opening of unnamed modules. - if ((!scanSpec.ignoreParentClassLoaders && scanSpec.overrideClassLoaders == null - && scanSpec.overrideClasspath == null) - || (moduleFinder != null && moduleFinder.forceScanJavaClassPath()&& scanSpec.overrideClassLoaders == null - && scanSpec.overrideClasspath == null)) { + if ((!scanSpec.ignoreParentClassLoaders) || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null From 718bcba85c276cc7d4d6f5249d3e6451222840b6 Mon Sep 17 00:00:00 2001 From: Rob Oxspring Date: Sun, 7 Nov 2021 13:44:03 +0000 Subject: [PATCH 233/713] ModuleFinder understands which modules the user requested --- .../classgraph/classpath/ClasspathFinder.java | 50 +++++++++---------- .../classgraph/classpath/ModuleFinder.java | 22 +++++--- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index b07b22973..96fe05eb5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -118,29 +118,30 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); // If classloaders are overridden, check if the override classloader(s) is/are JPMS classloaders. - // If so, need to enable module scanning. If not, disable module scanning, since only the provided - // classloader(s) should be scanned. (#382) - boolean scanModules; - if (scanSpec.enableSystemJarsAndModules) { - // Always scan modules if system modules are enabled - scanModules = true; - } else if (scanSpec.overrideClasspath != null) { - // Don't scan modules if classpath is overridden - scanModules = false; + // If so, need to enable non-system module scanning. + boolean scanNonSystemModules; + if (scanSpec.overrideClasspath != null) { + // Don't scan non-system modules if classpath is overridden + scanNonSystemModules = false; } else if (scanSpec.overrideClassLoaders != null) { - // If classloaders are overridden, only scan modules if an override classloader is a JPMS + // If classloaders are overridden, only scan non-system modules if an override classloader is a JPMS // AppClassLoader or PlatformClassLoader - scanModules = false; + scanNonSystemModules = false; for (final ClassLoader classLoader : scanSpec.overrideClassLoaders) { final String classLoaderClassName = classLoader.getClass().getName(); // It's not possible to instantiate AppClassLoader or PlatformClassLoader, so if these are // passed in as override classloaders, they must have been obtained using // Thread.currentThread().getContextClassLoader() [.getParent()] or similar if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader")) { - scanModules = true; + if (!scanSpec.enableSystemJarsAndModules) { + if (classpathFinderLog != null) { + classpathFinderLog.log("overrideClassLoaders() was called with an instance of " + + "jdk.internal.loader.ClassLoaders$AppClassLoader, which is a system " + + "classloader, so enableSystemJarsAndModules() was called automatically"); + } + scanSpec.enableSystemJarsAndModules = true; + } } else if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { - scanModules = true; - // The platform classloader was passed in, so specifically enable system module scanning if (!scanSpec.enableSystemJarsAndModules) { if (classpathFinderLog != null) { classpathFinderLog.log("overrideClassLoaders() was called with an instance of " @@ -152,14 +153,15 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } } else { - // If classloaders are not overridden and classpath is not overridden, only scan modules + // If classloaders are not overridden and classpath is not overridden, only scan non-system modules // if module scanning is enabled - scanModules = scanSpec.scanModules; + scanNonSystemModules = scanSpec.scanModules; } - moduleFinder = scanModules + // Only instantiate a module finder if requested + moduleFinder = scanSpec.enableSystemJarsAndModules || scanSpec.scanModules ? new ModuleFinder(CallStackReader.getClassContext(classpathFinderLog), scanSpec, - classpathFinderLog) + scanNonSystemModules, classpathFinderLog) : null; classpathOrder = new ClasspathOrder(scanSpec); @@ -284,15 +286,13 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); } - // Never scan java.class.path if classloaders or classpath are overridden - if (scanSpec.overrideClassLoaders != null || scanSpec.overrideClasspath != null) { - return; - } - - // Scan java.class.path if parent classloaders are not ignored, or if module scanning was enabled and an unnamed module + // Only scan java.class.path if parent classloaders are not ignored, classloaders are not overridden, + // and the classpath is not overridden, unless only module scanning was enabled, and an unnamed module // layer was encountered -- in this case, have to forcibly scan java.class.path, since the ModuleLayer // API doesn't allow for the opening of unnamed modules. - if ((!scanSpec.ignoreParentClassLoaders) || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { + if ((!scanSpec.ignoreParentClassLoaders && scanSpec.overrideClassLoaders == null + && scanSpec.overrideClasspath == null) + || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index 6a98b3645..f29c92697 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -210,12 +210,14 @@ private static List findModuleRefs(final LinkedHashSet layers * the call stack * @param scanSpec * the scan spec + * @param scanNonSystemModules + * whether to include unnamed and non-system modules * @param log * the log * @return the list */ private List findModuleRefsFromCallstack(final Class[] callStack, final ScanSpec scanSpec, - final LogNode log) { + boolean scanNonSystemModules, final LogNode log) { final LinkedHashSet layers = new LinkedHashSet<>(); if (callStack != null) { for (final Class stackFrameClass : callStack) { @@ -226,7 +228,7 @@ private List findModuleRefsFromCallstack(final Class[] callStack, module, "getLayer"); if (layer != null) { layers.add(layer); - } else { + } else if (scanNonSystemModules) { // getLayer() returns null for unnamed modules -- still add null to list if it is returned, // so we can get classes from java.class.path forceScanJavaClassPath = true; @@ -246,7 +248,7 @@ private List findModuleRefsFromCallstack(final Class[] callStack, .invokeStaticMethod(/* throwException = */ false, moduleLayerClass, "boot"); if (bootLayer != null) { layers.add(bootLayer); - } else { + } else if (scanNonSystemModules) { // getLayer() returns null for unnamed modules -- still add null to list if it is returned, // so we can get classes from java.class.path. (I'm not sure if the boot layer can ever // actually be null, but this is here for completeness.) @@ -265,17 +267,19 @@ private List findModuleRefsFromCallstack(final Class[] callStack, * the callstack. * @param scanSpec * The scan spec. + * @param scanNonSystemModules + * whether to include unnamed and non-system modules * @param log * The log. */ - public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final LogNode log) { + public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, boolean scanNonSystemModules, final LogNode log) { if (scanSpec.scanModules) { // Get the module resolution order List allModuleRefsList = null; if (scanSpec.overrideModuleLayers == null) { // Find module references for classes on callstack, and from system (for JDK9+) if (callStack != null && callStack.length > 0) { - allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, log); + allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, scanNonSystemModules, log); } } else { if (log != null) { @@ -294,9 +298,13 @@ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final L for (final ModuleRef moduleRef : allModuleRefsList) { if (moduleRef != null) { if (moduleRef.isSystemModule()) { - systemModuleRefs.add(moduleRef); + if (scanSpec.enableSystemJarsAndModules) { + systemModuleRefs.add(moduleRef); + } } else { - nonSystemModuleRefs.add(moduleRef); + if (scanNonSystemModules) { + nonSystemModuleRefs.add(moduleRef); + } } } } From b7520b4b6f43b46b9fcf7806d7c49ab7ddbdeede Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 02:02:16 +0000 Subject: [PATCH 234/713] Bump jvm-driver from 8.2.3 to 8.4.1 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.2.3 to 8.4.1. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.2.3...jvm-driver-8.4.1) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 689364cd8..b99590e01 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.2.3 + 8.4.1 true From 809a600e34a31854c9271627b8d44a4e46744acb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 7 Nov 2021 21:29:39 -0700 Subject: [PATCH 235/713] Source > Cleanup --- .../classgraph/classpath/ModuleFinder.java | 5 +- .../classpath/ClasspathFinderTest.java | 57 +++++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index f29c92697..777c16f1d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -217,7 +217,7 @@ private static List findModuleRefs(final LinkedHashSet layers * @return the list */ private List findModuleRefsFromCallstack(final Class[] callStack, final ScanSpec scanSpec, - boolean scanNonSystemModules, final LogNode log) { + final boolean scanNonSystemModules, final LogNode log) { final LinkedHashSet layers = new LinkedHashSet<>(); if (callStack != null) { for (final Class stackFrameClass : callStack) { @@ -272,7 +272,8 @@ private List findModuleRefsFromCallstack(final Class[] callStack, * @param log * The log. */ - public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, boolean scanNonSystemModules, final LogNode log) { + public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final boolean scanNonSystemModules, + final LogNode log) { if (scanSpec.scanModules) { // Get the module resolution order List allModuleRefsList = null; diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java index cd77f29d4..f5def903d 100644 --- a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -1,11 +1,9 @@ package nonapi.io.github.classgraph.classpath; -import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.utils.LogNode; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.JRE; -import org.junit.jupiter.api.io.TempDir; +import static nonapi.io.github.classgraph.classpath.SystemJarFinder.getJreRtJarPath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URL; import java.net.URLClassLoader; @@ -15,10 +13,13 @@ import java.util.Set; import java.util.TreeSet; -import static nonapi.io.github.classgraph.classpath.SystemJarFinder.getJreRtJarPath; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.io.TempDir; + +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.LogNode; public class ClasspathFinderTest { @@ -30,7 +31,7 @@ public class ClasspathFinderTest { */ @Test @EnabledForJreRange(max = JRE.JAVA_8) - public void testOverrideClasspathAndEnableSystemJars(@TempDir Path tmpDir) throws Exception { + public void testOverrideClasspathAndEnableSystemJars(@TempDir final Path tmpDir) throws Exception { // Arrange final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); final ScanSpec scanSpec = new ScanSpec(); @@ -43,13 +44,12 @@ public void testOverrideClasspathAndEnableSystemJars(@TempDir Path tmpDir) throw // Assert final Set paths = new TreeSet<>(); - for (final String path : classpathFinder - .getClasspathOrder() - .getClasspathEntryUniqueResolvedPaths()) { + for (final String path : classpathFinder.getClasspathOrder().getClasspathEntryUniqueResolvedPaths()) { paths.add(Paths.get(path)); } assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); - assertTrue(paths.remove(Paths.get(getJreRtJarPath())), "Classpath should have contained system jars: " + paths); + assertTrue(paths.remove(Paths.get(getJreRtJarPath())), + "Classpath should have contained system jars: " + paths); assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); } @@ -61,26 +61,25 @@ public void testOverrideClasspathAndEnableSystemJars(@TempDir Path tmpDir) throw */ @Test @EnabledForJreRange(max = JRE.JAVA_8) - public void testOverrideClassLoaderAndEnableSystemJars(@TempDir Path tmpDir) throws Exception { + public void testOverrideClassLoaderAndEnableSystemJars(@TempDir final Path tmpDir) throws Exception { // Arrange final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); final ScanSpec scanSpec = new ScanSpec(); scanSpec.enableSystemJarsAndModules = true; scanSpec.ignoreParentClassLoaders = true; - scanSpec.overrideClassLoaders(new URLClassLoader(new URL[]{classesDir.toUri().toURL()})); + scanSpec.overrideClassLoaders(new URLClassLoader(new URL[] { classesDir.toUri().toURL() })); // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); // Assert final Set paths = new TreeSet<>(); - for (final String path : classpathFinder - .getClasspathOrder() - .getClasspathEntryUniqueResolvedPaths()) { + for (final String path : classpathFinder.getClasspathOrder().getClasspathEntryUniqueResolvedPaths()) { paths.add(Paths.get(path)); } assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); - assertTrue(paths.remove(Paths.get(getJreRtJarPath())), "Classpath should have contained system jars: " + paths); + assertTrue(paths.remove(Paths.get(getJreRtJarPath())), + "Classpath should have contained system jars: " + paths); assertEquals(0, paths.size(), "Classpath should have no other entries: " + paths); } @@ -92,13 +91,13 @@ public void testOverrideClassLoaderAndEnableSystemJars(@TempDir Path tmpDir) thr */ @Test @EnabledForJreRange(min = JRE.JAVA_9) - public void testOverrideClasspathAndEnableSystemModules(@TempDir Path tmpDir) throws Exception { + public void testOverrideClasspathAndEnableSystemModules(@TempDir final Path tmpDir) throws Exception { // Arrange final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); final ScanSpec scanSpec = new ScanSpec(); scanSpec.enableSystemJarsAndModules = true; scanSpec.ignoreParentClassLoaders = true; - scanSpec.overrideClasspath = Collections.singletonList(classesDir); + scanSpec.overrideClasspath = Collections. singletonList(classesDir); // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); @@ -109,9 +108,7 @@ public void testOverrideClasspathAndEnableSystemModules(@TempDir Path tmpDir) th assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); final Set paths = new TreeSet<>(); - for (final String path : classpathFinder - .getClasspathOrder() - .getClasspathEntryUniqueResolvedPaths()) { + for (final String path : classpathFinder.getClasspathOrder().getClasspathEntryUniqueResolvedPaths()) { paths.add(Paths.get(path)); } assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); @@ -126,13 +123,13 @@ public void testOverrideClasspathAndEnableSystemModules(@TempDir Path tmpDir) th */ @Test @EnabledForJreRange(min = JRE.JAVA_9) - public void testOverrideClassLoaderAndEnableSystemModules(@TempDir Path tmpDir) throws Exception { + public void testOverrideClassLoaderAndEnableSystemModules(@TempDir final Path tmpDir) throws Exception { // Arrange final Path classesDir = tmpDir.toAbsolutePath().normalize().toRealPath(); final ScanSpec scanSpec = new ScanSpec(); scanSpec.enableSystemJarsAndModules = true; scanSpec.ignoreParentClassLoaders = true; - scanSpec.overrideClassLoaders(new URLClassLoader(new URL[]{classesDir.toUri().toURL()})); + scanSpec.overrideClassLoaders(new URLClassLoader(new URL[] { classesDir.toUri().toURL() })); // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); @@ -143,9 +140,7 @@ public void testOverrideClassLoaderAndEnableSystemModules(@TempDir Path tmpDir) assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); final Set paths = new TreeSet<>(); - for (final String path : classpathFinder - .getClasspathOrder() - .getClasspathEntryUniqueResolvedPaths()) { + for (final String path : classpathFinder.getClasspathOrder().getClasspathEntryUniqueResolvedPaths()) { paths.add(Paths.get(path)); } assertTrue(paths.remove(classesDir), "Classpath should have contained " + classesDir + ": " + paths); From aff50c4ab361e3e379c6a1000ce0550b3307b7c2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 7 Nov 2021 21:59:23 -0700 Subject: [PATCH 236/713] A few tweaks to PR #592 --- .../classgraph/classpath/ClasspathFinder.java | 7 +- .../classgraph/classpath/ModuleFinder.java | 82 +++++++++---------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 96fe05eb5..3cc862ce9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -159,9 +159,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } // Only instantiate a module finder if requested - moduleFinder = scanSpec.enableSystemJarsAndModules || scanSpec.scanModules + moduleFinder = scanNonSystemModules || scanSpec.enableSystemJarsAndModules ? new ModuleFinder(CallStackReader.getClassContext(classpathFinderLog), scanSpec, - scanNonSystemModules, classpathFinderLog) + scanNonSystemModules, /* scanSystemModules = */ scanSpec.enableSystemJarsAndModules, + classpathFinderLog) : null; classpathOrder = new ClasspathOrder(scanSpec); @@ -194,7 +195,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = contextClassLoaders; } - // If system jars are enabled, add JRE rt.jar to the beginning of the classpath + // If system jars and modules are enabled, add JRE rt.jar to the beginning of the classpath if (scanSpec.enableSystemJarsAndModules) { final String jreRtJar = SystemJarFinder.getJreRtJarPath(); diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index 777c16f1d..e5467be32 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -268,51 +268,49 @@ private List findModuleRefsFromCallstack(final Class[] callStack, * @param scanSpec * The scan spec. * @param scanNonSystemModules - * whether to include unnamed and non-system modules + * whether to scan unnamed and non-system modules + * @param scanSystemModules + * whether to scan system modules * @param log * The log. */ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final boolean scanNonSystemModules, - final LogNode log) { - if (scanSpec.scanModules) { - // Get the module resolution order - List allModuleRefsList = null; - if (scanSpec.overrideModuleLayers == null) { - // Find module references for classes on callstack, and from system (for JDK9+) - if (callStack != null && callStack.length > 0) { - allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, scanNonSystemModules, log); - } - } else { - if (log != null) { - final LogNode subLog = log.log("Overriding module layers"); - for (final Object moduleLayer : scanSpec.overrideModuleLayers) { - subLog.log(moduleLayer.toString()); - } + final boolean scanSystemModules, final LogNode log) { + // Get the module resolution order + List allModuleRefsList = null; + if (scanSpec.overrideModuleLayers == null) { + // Find module references for classes on callstack, and from system (for JDK9+) + if (callStack != null && callStack.length > 0) { + allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, scanNonSystemModules, log); + } + } else { + if (log != null) { + final LogNode subLog = log.log("Overriding module layers"); + for (final Object moduleLayer : scanSpec.overrideModuleLayers) { + subLog.log(moduleLayer.toString()); } - allModuleRefsList = findModuleRefs(new LinkedHashSet<>(scanSpec.overrideModuleLayers), scanSpec, - log); } - if (allModuleRefsList != null) { - // Split modules into system modules and non-system modules - systemModuleRefs = new ArrayList<>(); - nonSystemModuleRefs = new ArrayList<>(); - for (final ModuleRef moduleRef : allModuleRefsList) { - if (moduleRef != null) { - if (moduleRef.isSystemModule()) { - if (scanSpec.enableSystemJarsAndModules) { - systemModuleRefs.add(moduleRef); - } - } else { - if (scanNonSystemModules) { - nonSystemModuleRefs.add(moduleRef); - } - } + allModuleRefsList = findModuleRefs(new LinkedHashSet<>(scanSpec.overrideModuleLayers), scanSpec, log); + } + if (allModuleRefsList != null) { + // Split modules into system modules and non-system modules + systemModuleRefs = new ArrayList<>(); + nonSystemModuleRefs = new ArrayList<>(); + for (final ModuleRef moduleRef : allModuleRefsList) { + if (moduleRef != null) { + final boolean isSystemModule = moduleRef.isSystemModule(); + if (isSystemModule && scanSystemModules) { + systemModuleRefs.add(moduleRef); + } else if (!isSystemModule && scanNonSystemModules) { + nonSystemModuleRefs.add(moduleRef); } } } - // Log any identified modules - if (log != null) { - final LogNode sysSubLog = log.log("Found system modules:"); + } + // Log any identified modules + if (log != null) { + if (scanSystemModules) { + final LogNode sysSubLog = log.log("System modules found:"); if (systemModuleRefs != null && !systemModuleRefs.isEmpty()) { for (final ModuleRef moduleRef : systemModuleRefs) { sysSubLog.log(moduleRef.toString()); @@ -320,7 +318,11 @@ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final b } else { sysSubLog.log("[None]"); } - final LogNode nonSysSubLog = log.log("Found non-system modules:"); + } else { + log.log("Scanning of system modules is not enabled"); + } + if (scanNonSystemModules) { + final LogNode nonSysSubLog = log.log("Non-system modules found:"); if (nonSystemModuleRefs != null && !nonSystemModuleRefs.isEmpty()) { for (final ModuleRef moduleRef : nonSystemModuleRefs) { nonSysSubLog.log(moduleRef.toString()); @@ -328,10 +330,8 @@ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final b } else { nonSysSubLog.log("[None]"); } - } - } else { - if (log != null) { - log.log("Module scanning is disabled, because classloaders or classpath was overridden"); + } else { + log.log("Scanning of non-system modules is not enabled"); } } } From f1e7d17f7ba671c21eff4d8f0ad4e04bdf9f7fdc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 7 Nov 2021 21:59:48 -0700 Subject: [PATCH 237/713] [maven-release-plugin] prepare release classgraph-4.8.130 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b99590e01..ce011b12c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.130-SNAPSHOT + 4.8.130 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.130 From 910900dce2a272f64da893ea0b7ee9b2ea0b0eff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 7 Nov 2021 21:59:51 -0700 Subject: [PATCH 238/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ce011b12c..68cf66914 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.130 + 4.8.131-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.130 + HEAD From c36cf6c06285680bfed194d59f35275b5fb7c115 Mon Sep 17 00:00:00 2001 From: Knut Sander Date: Mon, 8 Nov 2021 09:59:50 +0100 Subject: [PATCH 239/713] fix AccessControlException from SecurityManager in ClasspathOrder In ClasspathOrder, class path elements are converted to URLs by pathElementURL = new File(pathElementToStr).toURI().toURL(). File#toUri() requires read access to the directory containing the file. If the JVM is running with SecurityManager enabled, this file system access may represent a security policy violation (e.g., in Tomcat, the `CATALINA_HOME/bin` folder containing classpath elements such as bootstrap.jar, etc.). In this case, the existing fallback handling can be used. --- .../io/github/classgraph/classpath/ClasspathOrder.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index edff4e036..376756b73 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -319,14 +319,18 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla final String pathElementToStr = pathElement.toString(); try { pathElementURL = new File(pathElementToStr).toURI().toURL(); - } catch (final MalformedURLException e) { + } catch (final MalformedURLException | SecurityException e) { + if (log != null) { + log.log("Failed to convert classpath element to URL, " + + "Try prepending \"file:\" to create a URL (" + e + "): " + pathElementStr ); + } // Final fallback -- try prepending "file:" to create a URL pathElementURL = new URL("file:" + pathElementToStr); } } - } catch (final MalformedURLException e1) { + } catch (final MalformedURLException |SecurityException e1) { if (log != null) { - log.log("Cannot convert to URL: " + pathElement); + log.log("Cannot convert to URL (" + e1 + "): " + pathElement ); } pathElementURL = null; } From c398d198d8646dc07019ef640352cb9cd2c9902c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 Nov 2021 23:00:11 -0700 Subject: [PATCH 240/713] Catch some additional unchecked exceptions (#594) --- .../java/io/github/classgraph/ClassInfo.java | 2 +- .../classgraph/ClasspathElementPathDir.java | 11 +++++++++- .../classgraph/ClasspathElementZip.java | 2 +- .../java/io/github/classgraph/Resource.java | 2 +- .../QuarkusClassLoaderHandler.java | 11 +++++++++- .../classgraph/classpath/ClasspathOrder.java | 20 ++++++++++++------- .../fastzipfilereader/NestedJarHandler.java | 6 +++--- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 876ef9243..c546c46a8 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2840,7 +2840,7 @@ public URI getClasspathElementURI() { public URL getClasspathElementURL() { try { return getClasspathElementURI().toURL(); - } catch (final MalformedURLException e) { + } catch (final IllegalArgumentException | MalformedURLException e) { throw new IllegalArgumentException("Could not get classpath element URL", e); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index c46ec080c..ff5465018 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -568,7 +569,15 @@ public File getFile() { */ @Override URI getURI() { - return packageRootPath.toUri(); + try { + return packageRootPath.toUri(); + } catch (IOError | SecurityException e) { + try { + return new URI("file:" + packageRootPath); + } catch (final URISyntaxException e1) { + throw new IllegalArgumentException("Could not convert to URI: " + packageRootPath); + } + } } @Override diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index f527f5d2e..19529aa88 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -117,7 +117,7 @@ class ClasspathElementZip extends ClasspathElement { // Path.toString does not include URI scheme => turn into a URI so that toString works try { rawPath = ((Path) rawPathObj).toUri().toString(); - } catch (final IOError e) { + } catch (final IOError | SecurityException e) { // Fall through } } diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 40b262df6..4848660a9 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -101,7 +101,7 @@ public Resource(final ClasspathElement classpathElement, final long length) { private static URL uriToURL(final URI uri) { try { return uri.toURL(); - } catch (final MalformedURLException e) { + } catch (final IllegalArgumentException | MalformedURLException e) { if (uri.getScheme().equals("jrt")) { // Currently URL cannot handle the "jrt:" scheme, used by system modules. throw new IllegalArgumentException("Could not create URL from URI with \"jrt:\" scheme " diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index faa9ced09..d03cfebf3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -28,6 +28,8 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import java.io.IOError; +import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.Map; @@ -136,7 +138,14 @@ private static void findClasspathOrderForRuntimeClassloader(final ClassLoader cl classLoader, "applicationClassDirectories"); if (applicationClassDirectories != null) { for (final Path path : applicationClassDirectories) { - classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log); + try { + final URI uri = path.toUri(); + classpathOrder.addClasspathEntryObject(uri, classLoader, scanSpec, log); + } catch (IOError | SecurityException e) { + if (log != null) { + log.log("Could not convert path to URI: " + path); + } + } } } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 376756b73..7e028264f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classpath; import java.io.File; +import java.io.IOError; import java.lang.reflect.Array; import java.net.MalformedURLException; import java.net.URI; @@ -301,9 +302,14 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElement == null) { return false; } - // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped - String pathElementStr = pathElement instanceof Path ? ((Path) pathElement).toUri().toString() - : pathElement.toString(); + String pathElementStr; + try { + // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped + pathElementStr = pathElement instanceof Path ? ((Path) pathElement).toUri().toString() + : pathElement.toString(); + } catch (IOError | SecurityException e) { + pathElementStr = pathElement.toString(); + } pathElementStr = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStr); if (pathElementStr.isEmpty()) { return false; @@ -319,18 +325,18 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla final String pathElementToStr = pathElement.toString(); try { pathElementURL = new File(pathElementToStr).toURI().toURL(); - } catch (final MalformedURLException | SecurityException e) { + } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e) { if (log != null) { log.log("Failed to convert classpath element to URL, " - + "Try prepending \"file:\" to create a URL (" + e + "): " + pathElementStr ); + + "Try prepending \"file:\" to create a URL (" + e + "): " + pathElementStr); } // Final fallback -- try prepending "file:" to create a URL pathElementURL = new URL("file:" + pathElementToStr); } } - } catch (final MalformedURLException |SecurityException e1) { + } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e1) { if (log != null) { - log.log("Cannot convert to URL (" + e1 + "): " + pathElement ); + log.log("Cannot convert to URL (" + e1 + "): " + pathElement); } pathElementURL = null; } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 2a1597016..c845fff0c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -521,7 +521,7 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } catch (final MalformedURLException e1) { try { url = new URI(jarURL).toURL(); - } catch (final URISyntaxException e2) { + } catch (final MalformedURLException | IllegalArgumentException | URISyntaxException e2) { throw new IOException("Could not parse URL: " + jarURL); } } @@ -539,8 +539,8 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } // Wrap Path in PhysicalZipFile and return it return new PhysicalZipFile(path, this, log); - } catch (final URISyntaxException e) { - throw new IOException("Could not convert URL to URI: " + url); + } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { + throw new IOException("Could not convert URL to URI (" + e + "): " + url); } catch (final FileSystemNotFoundException e) { // Not a custom filesystem } From 8596d556f1a395f0ced951fb4424839d42aa62fb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 Nov 2021 23:01:32 -0700 Subject: [PATCH 241/713] [maven-release-plugin] prepare release classgraph-4.8.131 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 68cf66914..c5466999e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.131-SNAPSHOT + 4.8.131 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.131 From f350019b68f3002e1797390adfb5e9a89d4ee4bf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 Nov 2021 23:01:35 -0700 Subject: [PATCH 242/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c5466999e..537f16baf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.131 + 4.8.132-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.131 + HEAD From 5dfdada03763a459d0a1d94d93cf61bf2d317eb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Nov 2021 02:01:31 +0000 Subject: [PATCH 243/713] Bump jvm-driver from 8.4.1 to 8.5.0 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.4.1 to 8.5.0. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.4.1...jvm-driver-8.5.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 537f16baf..c63957b10 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.4.1 + 8.5.0 true From 70bff06b5913b5d0e263bdbcf31ec15b556b595f Mon Sep 17 00:00:00 2001 From: Tobias Niehues Date: Fri, 12 Nov 2021 14:50:39 +0100 Subject: [PATCH 244/713] Mark resolution of jdk.internal.misc as optional --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c63957b10..92450ac0d 100644 --- a/pom.xml +++ b/pom.xml @@ -489,7 +489,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc,io.github.toolfactory.jvm;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" true From 626dc9441200bf3d815cdd686d9abf2406351815 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Nov 2021 15:34:16 -0700 Subject: [PATCH 245/713] Add missing requires for jvm-driver --- src/module-info/io.github.classgraph/module-info.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/module-info/io.github.classgraph/module-info.java b/src/module-info/io.github.classgraph/module-info.java index f20e2c5da..4901edf83 100644 --- a/src/module-info/io.github.classgraph/module-info.java +++ b/src/module-info/io.github.classgraph/module-info.java @@ -47,6 +47,7 @@ // LogNode requires java.logging requires java.logging; - // ReflectionUtils may use io.github.toolfactory.narcissus if it is available + // ReflectionUtils may use narcissus or jvm-driver, if available requires static io.github.toolfactory.narcissus; + requires static io.github.toolfactory.jvm; } From 69488c1c75483f2e86d542ac230557556b9e9439 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Nov 2021 15:38:40 -0700 Subject: [PATCH 246/713] Add missing Narcissus dep --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92450ac0d..10a896847 100644 --- a/pom.xml +++ b/pom.xml @@ -489,7 +489,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional,io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" true From 81800767c353e9fa92b9671d301fd215536c85a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Nov 2021 15:39:30 -0700 Subject: [PATCH 247/713] [maven-release-plugin] prepare release classgraph-4.8.132 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 10a896847..df9d23797 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.132-SNAPSHOT + 4.8.132 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.132 From 23cce49704f0f1c3e43dc0c23b3b6cbe29ca7d96 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Nov 2021 15:39:32 -0700 Subject: [PATCH 248/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index df9d23797..55dd8d8b1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.132 + 4.8.133-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.132 + HEAD From 5fafca3d46007718ccf22245c2304627ee0cb097 Mon Sep 17 00:00:00 2001 From: Roman Skripka Date: Tue, 16 Nov 2021 11:01:03 +0100 Subject: [PATCH 249/713] Fix sun.nio.ch import --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55dd8d8b1..7eed959ad 100644 --- a/pom.xml +++ b/pom.xml @@ -489,7 +489,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional,io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" true From cb37d6c2faa5e77c1f524af2740e776c8f5684f9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 16 Nov 2021 12:49:11 -0700 Subject: [PATCH 250/713] [maven-release-plugin] prepare release classgraph-4.8.133 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7eed959ad..445b62789 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.133-SNAPSHOT + 4.8.133 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.133 From 6e994db9296af8611467d5b0ca88f8284fb2c0dd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 16 Nov 2021 12:49:13 -0700 Subject: [PATCH 251/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 445b62789..04b8d92c0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.133 + 4.8.134-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.133 + HEAD From 9133da24c5ad40fe8989c66c1723ee5bd234dc37 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 16 Nov 2021 12:56:17 -0700 Subject: [PATCH 252/713] Remove obsolete jdk.internal.misc OSGi dependency (#597) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04b8d92c0..8a43d85e4 100644 --- a/pom.xml +++ b/pom.xml @@ -489,7 +489,7 @@ io.github.classgraph;version="${project.version}" - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" true From 2829313e17aa31417f51e2f80174065629d120d9 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 19 Nov 2021 15:20:03 +0000 Subject: [PATCH 253/713] Closing ClassfileReader must also close its underlying Resource. --- .../classgraph/ClasspathElementFileDir.java | 27 ++--- .../classgraph/ClasspathElementModule.java | 17 +++- .../classgraph/ClasspathElementPathDir.java | 27 ++--- .../classgraph/ClasspathElementZip.java | 26 ++--- .../io/github/classgraph/fileslice/Slice.java | 2 +- .../fileslice/reader/ClassfileReader.java | 15 ++- .../issues/issue600/Issue600Test.java | 98 +++++++++++++++++++ 7 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index b2db44e0d..2584e9345 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -155,7 +155,6 @@ void open(final WorkQueue workQueue, final LogNode log) "Skipping classpath element, since dir cannot be accessed: " + classpathEltDir, log); } skipClasspathElement = true; - return; } } @@ -177,7 +176,7 @@ private Resource newResource(final String pathRelativeToPackageRoot, final File private FileSlice fileSlice; /** True if the resource is open. */ - protected AtomicBoolean isOpen = new AtomicBoolean(); + private final AtomicBoolean isOpen = new AtomicBoolean(); @Override public String getPath() { @@ -247,7 +246,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new FileSlice and then open it fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); length = fileSlice.sliceLength; - return new ClassfileReader(fileSlice); + return new ClassfileReader(fileSlice, onClose()); } @Override @@ -261,14 +260,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - inputStream = fileSlice.open(new Runnable() { - @Override - public void run() { - if (isOpen.getAndSet(false)) { - close(); - } - } - }); + inputStream = fileSlice.open(onClose()); length = fileSlice.sliceLength; return inputStream; } @@ -286,7 +278,6 @@ public byte[] load() throws IOException { @Override public void close() { - super.close(); // Close inputStream if (isOpen.getAndSet(false)) { if (byteBuffer != null) { // Any ByteBuffer ref should be a duplicate, so it doesn't need to be cleaned @@ -298,6 +289,18 @@ public void close() { fileSlice = null; } } + super.close(); // Close inputStream + } + + private Runnable onClose() { + return new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index cac5afd3e..4e81c752f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -140,7 +140,7 @@ private Resource newResource(final String resourcePath) { private ModuleReaderProxy moduleReaderProxy; /** True if the resource is open. */ - protected AtomicBoolean isOpen = new AtomicBoolean(); + private final AtomicBoolean isOpen = new AtomicBoolean(); @Override public String getPath() { @@ -188,7 +188,7 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open()); + return new ClassfileReader(open(), onClose()); } @Override @@ -233,7 +233,6 @@ public byte[] load() throws IOException { @Override public void close() { - super.close(); // Close inputStream if (isOpen.getAndSet(false) && moduleReaderProxy != null) { if (byteBuffer != null) { // Release any open ByteBuffer @@ -246,6 +245,18 @@ public void close() { // ClasspathElementModule#close(). moduleReaderProxy = null; } + super.close(); // Close inputStream + } + + private Runnable onClose() { + return new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index ff5465018..687876341 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -157,7 +157,6 @@ void open(final WorkQueue workQueue, final LogNode log) "Skipping classpath element, since dir cannot be accessed: " + classpathEltPath, log); } skipClasspathElement = true; - return; } } @@ -182,7 +181,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes private PathSlice pathSlice; /** True if the resource is open. */ - protected AtomicBoolean isOpen = new AtomicBoolean(); + private final AtomicBoolean isOpen = new AtomicBoolean(); @Override public String getPath() { @@ -253,7 +252,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new PathSlice and then open it pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; - return new ClassfileReader(pathSlice); + return new ClassfileReader(pathSlice, onClose()); } @Override @@ -267,14 +266,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } pathSlice = new PathSlice(resourcePath, nestedJarHandler); - inputStream = pathSlice.open(new Runnable() { - @Override - public void run() { - if (isOpen.getAndSet(false)) { - close(); - } - } - }); + inputStream = pathSlice.open(onClose()); length = pathSlice.sliceLength; return inputStream; } @@ -292,7 +284,6 @@ public byte[] load() throws IOException { @Override public void close() { - super.close(); // Close inputStream if (isOpen.getAndSet(false)) { if (byteBuffer != null) { // Any ByteBuffer ref should be a duplicate, so it doesn't need to be cleaned @@ -304,6 +295,18 @@ public void close() { pathSlice = null; } } + super.close(); // Close inputStream + } + + private Runnable onClose() { + return new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 19529aa88..b11efb829 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -316,7 +316,7 @@ void open(final WorkQueue workQueue, final LogNode log) private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { return new Resource(this, zipEntry.uncompressedSize) { /** True if the resource is open. */ - protected AtomicBoolean isOpen = new AtomicBoolean(); + private final AtomicBoolean isOpen = new AtomicBoolean(); /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or @@ -391,14 +391,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { - inputStream = zipEntry.getSlice().open(new Runnable() { - @Override - public void run() { - if (isOpen.getAndSet(false)) { - close(); - } - } - }); + inputStream = zipEntry.getSlice().open(onClose()); length = zipEntry.uncompressedSize; return inputStream; @@ -410,7 +403,7 @@ public void run() { @Override ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open()); + return new ClassfileReader(open(), onClose()); } @Override @@ -452,12 +445,23 @@ public byte[] load() throws IOException { @Override public void close() { - super.close(); // Close inputStream if (isOpen.getAndSet(false) && byteBuffer != null) { // ByteBuffer should be a duplicate or slice, or should wrap an array, so it doesn't // need to be unmapped byteBuffer = null; } + super.close(); // Close inputStream + } + + private Runnable onClose() { + return new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; } }; } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java index 8a4f29c20..d5b3e056b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -263,7 +263,7 @@ public ClassfileReader openClassfileReader() throws IOException { throw new IllegalArgumentException( "Cannot open slices larger than 2GB for sequential buffered reading"); } - return new ClassfileReader(this); + return new ClassfileReader(this, null); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java index 8326940ec..cd59e8244 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java @@ -73,6 +73,8 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl */ private int classfileLengthHint = -1; + private final Runnable onClose; + /** * Initial buffer size. For most classfiles, only the first 16-64kb needs to be read (we don't read the * bytecodes). @@ -93,11 +95,14 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl * * @param slice * the {@link Slice} to read. + * @param onClose + * handler executed by {@link ClassfileReader#close}. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final Slice slice) throws IOException { + public ClassfileReader(final Slice slice, final Runnable onClose) throws IOException { this.classfileLengthHint = (int) slice.sliceLength; + this.onClose = onClose; if (slice.isDeflatedZipEntry) { // If this is a deflated slice, need to read from an InflaterInputStream to fill buffer inflaterInputStream = slice.open(); @@ -133,12 +138,15 @@ public ClassfileReader(final Slice slice) throws IOException { * * @param inputStream * the {@link InputStream} to read from. + * @param onClose + * handler executed by {@link ClassfileReader#close}. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final InputStream inputStream) throws IOException { + public ClassfileReader(final InputStream inputStream, final Runnable onClose) throws IOException { inflaterInputStream = inputStream; arr = new byte[INITIAL_BUF_SIZE]; + this.onClose = onClose; } /** @@ -443,6 +451,9 @@ public void close() { if (inflaterInputStream != null) { inflaterInputStream.close(); } + if (onClose != null) { + onClose.run(); + } } catch (final Exception e) { // Ignore } diff --git a/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java b/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java new file mode 100644 index 000000000..b65df7bdd --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java @@ -0,0 +1,98 @@ +package io.github.classgraph.issues.issue600; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.Resource; +import io.github.classgraph.ResourceList; +import io.github.classgraph.ScanResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class Issue600Test { + private static final int BUFFER_SIZE = 8192; + private static final int EOF = -1; + + private final ClassGraph classGraph = new ClassGraph() + .enableClassInfo() + .acceptPackages(getClass().getPackage().getName()); + + @Test + void testResourcesCanBeOpened() { + try (ScanResult scanResult = classGraph.scan()) { + ResourceList resources = scanResult.getAllResources(); + assertFalse(resources.isEmpty(), "Test is meaningless without resources to open."); + + // Check we can open the resources. + assertOpenCloseResources(resources); + + // And check we can reopen the resources. + assertOpenCloseResources(resources); + } + } + + @Test + void testResourcesCanBeRead() { + try (ScanResult scanResult = classGraph.scan()) { + ResourceList resources = scanResult.getAllResources(); + assertFalse(resources.isEmpty(), "Test is meaningless without resources to open."); + + // Check we can read the resources. + assertReadCloseResources(resources); + + // Check we can reread the resources. + assertReadCloseResources(resources); + } + } + + private void assertOpenCloseResources(ResourceList resources) { + for (final Resource resource : resources) { + assertDoesNotThrow(new Executable() { + @Override + public void execute() throws Throwable { + try (InputStream input = resource.open()) { + assertThat(consume(input)).isGreaterThan(0); + } + } + }, "Resource " + resource.getPath() + " should be closed."); + } + } + + private int consume(InputStream input) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int totalBytes = 0; + int bytesRead; + while ((bytesRead = input.read(buffer)) != EOF) { + totalBytes += bytesRead; + } + return totalBytes; + } + + private void assertReadCloseResources(ResourceList resources) { + for (final Resource resource : resources) { + assertDoesNotThrow(new Executable() { + @Override + public void execute() throws Throwable { + ByteBuffer buffer = resource.read(); + try { + assertTrue(buffer.hasRemaining()); + } finally { + resource.close(); + } + } + }, "Resource " + resource.getPath() + " should be closed."); + } + } + + public interface Api {} + + @SuppressWarnings("unused") + public static class Example implements Api {} +} From c8d8f8a542b429f80cfdc486114442630af5eec9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 01:37:31 -0700 Subject: [PATCH 254/713] Remove unused method --- .../io/github/classgraph/fileslice/Slice.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java index d5b3e056b..b4ea19bdc 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -36,10 +36,8 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.zip.Inflater; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; -import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.FileUtils; @@ -250,22 +248,6 @@ public void close() { */ public abstract RandomAccessReader randomAccessReader(); - /** - * Open this {@link Slice} for buffered sequential reading. Make sure you close this when you have finished with - * it, in order to recycle any {@link Inflater} instances. - * - * @return the classfile reader - * @throws IOException - * Signals that an I/O exception has occurred. - */ - public ClassfileReader openClassfileReader() throws IOException { - if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { - throw new IllegalArgumentException( - "Cannot open slices larger than 2GB for sequential buffered reading"); - } - return new ClassfileReader(this, null); - } - /** * Load the slice as a byte array. * From fe6620ea37d68527c71473fb2aba3fb9a72418d9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 01:45:13 -0700 Subject: [PATCH 255/713] Code cleanup --- .../fileslice/reader/ClassfileReader.java | 9 ++-- .../issues/issue600/Issue600Test.java | 46 ++++++++++--------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java index cd59e8244..7c98ec6d2 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java @@ -73,7 +73,8 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl */ private int classfileLengthHint = -1; - private final Runnable onClose; + /** The handler executed by {@link ClassfileReader#close()}. */ + private Runnable onClose; /** * Initial buffer size. For most classfiles, only the first 16-64kb needs to be read (we don't read the @@ -96,7 +97,7 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl * @param slice * the {@link Slice} to read. * @param onClose - * handler executed by {@link ClassfileReader#close}. + * the handler executed by {@link ClassfileReader#close()}. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ @@ -139,7 +140,7 @@ public ClassfileReader(final Slice slice, final Runnable onClose) throws IOExcep * @param inputStream * the {@link InputStream} to read from. * @param onClose - * handler executed by {@link ClassfileReader#close}. + * the handler executed by {@link ClassfileReader#close()}. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ @@ -450,9 +451,11 @@ public void close() { try { if (inflaterInputStream != null) { inflaterInputStream.close(); + inflaterInputStream = null; } if (onClose != null) { onClose.run(); + onClose = null; } } catch (final Exception e) { // Ignore diff --git a/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java b/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java index b65df7bdd..65dd4caf5 100644 --- a/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java +++ b/src/test/java/io/github/classgraph/issues/issue600/Issue600Test.java @@ -1,33 +1,33 @@ package io.github.classgraph.issues.issue600; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.Resource; -import io.github.classgraph.ResourceList; -import io.github.classgraph.ScanResult; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.Resource; +import io.github.classgraph.ResourceList; +import io.github.classgraph.ScanResult; class Issue600Test { private static final int BUFFER_SIZE = 8192; private static final int EOF = -1; - private final ClassGraph classGraph = new ClassGraph() - .enableClassInfo() - .acceptPackages(getClass().getPackage().getName()); + private final ClassGraph classGraph = new ClassGraph().enableClassInfo() + .acceptPackages(getClass().getPackage().getName()); @Test void testResourcesCanBeOpened() { try (ScanResult scanResult = classGraph.scan()) { - ResourceList resources = scanResult.getAllResources(); + final ResourceList resources = scanResult.getAllResources(); assertFalse(resources.isEmpty(), "Test is meaningless without resources to open."); // Check we can open the resources. @@ -41,7 +41,7 @@ void testResourcesCanBeOpened() { @Test void testResourcesCanBeRead() { try (ScanResult scanResult = classGraph.scan()) { - ResourceList resources = scanResult.getAllResources(); + final ResourceList resources = scanResult.getAllResources(); assertFalse(resources.isEmpty(), "Test is meaningless without resources to open."); // Check we can read the resources. @@ -52,7 +52,7 @@ void testResourcesCanBeRead() { } } - private void assertOpenCloseResources(ResourceList resources) { + private void assertOpenCloseResources(final ResourceList resources) { for (final Resource resource : resources) { assertDoesNotThrow(new Executable() { @Override @@ -65,8 +65,8 @@ public void execute() throws Throwable { } } - private int consume(InputStream input) throws IOException { - byte[] buffer = new byte[BUFFER_SIZE]; + private int consume(final InputStream input) throws IOException { + final byte[] buffer = new byte[BUFFER_SIZE]; int totalBytes = 0; int bytesRead; while ((bytesRead = input.read(buffer)) != EOF) { @@ -75,12 +75,12 @@ private int consume(InputStream input) throws IOException { return totalBytes; } - private void assertReadCloseResources(ResourceList resources) { + private void assertReadCloseResources(final ResourceList resources) { for (final Resource resource : resources) { assertDoesNotThrow(new Executable() { @Override public void execute() throws Throwable { - ByteBuffer buffer = resource.read(); + final ByteBuffer buffer = resource.read(); try { assertTrue(buffer.hasRemaining()); } finally { @@ -91,8 +91,10 @@ public void execute() throws Throwable { } } - public interface Api {} + public interface Api { + } @SuppressWarnings("unused") - public static class Example implements Api {} + public static class Example implements Api { + } } From e29e03ffc8dd84a362102ad81d0a2b0c728551fb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 01:45:51 -0700 Subject: [PATCH 256/713] [maven-release-plugin] prepare release classgraph-4.8.134 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8a43d85e4..358300155 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.134-SNAPSHOT + 4.8.134 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.134 From 6f5b9b42a239417361c676381c14e5b907a7e0dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 01:45:53 -0700 Subject: [PATCH 257/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 358300155..e2048b8f9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.134 + 4.8.135-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.134 + HEAD From f26085d871fd83fddedfd7d05b37e7413fd441aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 12:08:41 -0700 Subject: [PATCH 258/713] Close Resource if read() fails --- src/main/java/io/github/classgraph/ClasspathElementModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 4e81c752f..847768f38 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -216,8 +216,8 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { - read(); try (Resource res = this) { // Close this after use + read(); final byte[] byteArray; if (byteBuffer.hasArray() && byteBuffer.position() == 0 && byteBuffer.limit() == byteBuffer.capacity()) { From 387de7537f287a4257ebf9d8aff3efba851e2fec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 12:09:41 -0700 Subject: [PATCH 259/713] Set byteBuffer to null when it is released --- src/main/java/io/github/classgraph/ClasspathElementModule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 847768f38..677522d30 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -217,7 +217,7 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { try (Resource res = this) { // Close this after use - read(); + read(); // Fill byteBuffer final byte[] byteArray; if (byteBuffer.hasArray() && byteBuffer.position() == 0 && byteBuffer.limit() == byteBuffer.capacity()) { @@ -237,6 +237,7 @@ public void close() { if (byteBuffer != null) { // Release any open ByteBuffer moduleReaderProxy.release(byteBuffer); + byteBuffer = null; } // Recycle the (open) ModuleReaderProxy instance. moduleReaderProxyRecycler.recycle(moduleReaderProxy); From 547173216316de4d4eb02c695b602e453086db7f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 12:15:23 -0700 Subject: [PATCH 260/713] Close InputStream when module Resource is closed (#600) --- .../java/io/github/classgraph/ClasspathElementModule.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 677522d30..b29af2155 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -239,6 +239,14 @@ public void close() { moduleReaderProxy.release(byteBuffer); byteBuffer = null; } + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + // Ignore + } + inputStream = null; + } // Recycle the (open) ModuleReaderProxy instance. moduleReaderProxyRecycler.recycle(moduleReaderProxy); // Don't call ModuleReaderProxy#close(), leave the ModuleReaderProxy open in the recycler. From 3daa265e3b3182fccaa34bf83b8929c6564410cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 12:34:25 -0700 Subject: [PATCH 261/713] Revert last commit (#600) --- .../java/io/github/classgraph/ClasspathElementModule.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index b29af2155..677522d30 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -239,14 +239,6 @@ public void close() { moduleReaderProxy.release(byteBuffer); byteBuffer = null; } - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - // Ignore - } - inputStream = null; - } // Recycle the (open) ModuleReaderProxy instance. moduleReaderProxyRecycler.recycle(moduleReaderProxy); // Don't call ModuleReaderProxy#close(), leave the ModuleReaderProxy open in the recycler. From e44931b814e7d1d5f34994f75d1909c262611e47 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 13:15:14 -0700 Subject: [PATCH 262/713] Close Resource on InputStream close (#600) --- .../classgraph/ClasspathElementModule.java | 4 +- .../github/classgraph/ModuleReaderProxy.java | 130 ++++++++++++++---- 2 files changed, 107 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 677522d30..bc6e480b0 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -203,7 +203,7 @@ public InputStream open() throws IOException { } try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); - inputStream = moduleReaderProxy.open(resourcePath); + inputStream = moduleReaderProxy.open(resourcePath, onClose()); // Length cannot be obtained from ModuleReader length = -1L; return inputStream; @@ -217,7 +217,7 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { try (Resource res = this) { // Close this after use - read(); // Fill byteBuffer + read(); // Fill byteBuffer final byte[] byteArray; if (byteBuffer.hasArray() && byteBuffer.position() == 0 && byteBuffer.limit() == byteBuffer.capacity()) { diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index 8b0d9103a..c66f5d318 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -31,6 +31,7 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.List; @@ -124,44 +125,113 @@ public List list() throws SecurityException { * * @param path * The path to the resource to open. - * @param open - * if true, call moduleReader.open(name).get() (returning an InputStream), otherwise call - * moduleReader.read(name).get() (returning a ByteBuffer). + * @param onClose + * a method to run when the returned {@code InputStream} is closed, or null if none. + * * @return An {@link InputStream} for the content of the resource. * @throws SecurityException * If the module cannot be accessed. */ - private Object openOrRead(final String path, final boolean open) throws SecurityException { - final String methodName = open ? "open" : "read"; + public InputStream open(final String path, final Runnable onClose) throws SecurityException { final Object /* Optional */ optionalInputStream = ReflectionUtils - .invokeMethod(/* throwException = */ true, moduleReader, methodName, String.class, path); + .invokeMethod(/* throwException = */ true, moduleReader, "open", String.class, path); if (optionalInputStream == null) { - throw new IllegalArgumentException("Got null result from moduleReader." + methodName + "(name)"); + throw new IllegalArgumentException("Got null result from ModuleReader#open(String)"); } - final Object /* 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." + methodName + "(name).get()"); + throw new IllegalArgumentException("Got null result from ModuleReader#open(String)#get()"); } - return inputStream; - } + // Return a proxying InputStream that will close the Resource when the InputStream is closed (#600) + return new InputStream() { + @Override + public void close() throws IOException { + if (onClose != null) { + try { + onClose.run(); + } catch (final Exception e) { + // Ignore + } + } + inputStream.close(); + } - /** - * Use the proxied ModuleReader to open the named resource as an InputStream. - * - * @param path - * The path to the resource to open. - * @return An {@link InputStream} for the content of the resource. - * @throws SecurityException - * If the module cannot be accessed. - */ - public InputStream open(final String path) throws SecurityException { - return (InputStream) openOrRead(path, /* open = */ true); + @Override + public int read() throws IOException { + return inputStream.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + return inputStream.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + return inputStream.read(b, off, len); + } + + @Override + public byte[] readAllBytes() throws IOException { + return inputStream.readAllBytes(); + } + + @Override + public byte[] readNBytes(final int len) throws IOException { + return inputStream.readNBytes(len); + } + + @Override + public int readNBytes(final byte[] b, final int off, final int len) throws IOException { + return inputStream.readNBytes(b, off, len); + } + + @Override + public int available() throws IOException { + return inputStream.available(); + } + + @Override + public boolean markSupported() { + return inputStream.markSupported(); + } + + @Override + public synchronized void mark(final int readlimit) { + inputStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + inputStream.reset(); + } + + @Override + public long skip(final long n) throws IOException { + return inputStream.skip(n); + } + + @Override + public void skipNBytes(final long n) throws IOException { + inputStream.skipNBytes(n); + } + + @Override + public long transferTo(final OutputStream out) throws IOException { + return inputStream.transferTo(out); + } + + @Override + public String toString() { + return inputStream.toString(); + } + }; } /** - * Use the proxied ModuleReader to open the named resource as a ByteBuffer. Call release(byteBuffer) when you - * have finished with the ByteBuffer. + * Use the proxied ModuleReader to open the named resource as a ByteBuffer. Call {@link #release(ByteBuffer)} + * when you have finished with the ByteBuffer. * * @param path * The path to the resource to open. @@ -172,7 +242,17 @@ 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 { - return (ByteBuffer) openOrRead(path, /* open = */ false); + final Object /* Optional */ 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, + optionalByteBuffer, "get"); + if (byteBuffer == null) { + throw new IllegalArgumentException("Got null result from ModuleReader#read(String).get()"); + } + return byteBuffer; } /** From 9a6449f05e8624d20205925a9215627c304a934c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 14:18:47 -0700 Subject: [PATCH 263/713] JDK 7 compatibility fixes (#600) --- .../github/classgraph/ModuleReaderProxy.java | 87 +------- .../utils/InputStreamWithCloseAction.java | 210 ++++++++++++++++++ 2 files changed, 212 insertions(+), 85 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index c66f5d318..e6ab2e991 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -31,11 +31,11 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.List; import nonapi.io.github.classgraph.reflection.ReflectionUtils; +import nonapi.io.github.classgraph.utils.InputStreamWithCloseAction; /** A ModuleReader proxy, written using reflection to preserve backwards compatibility with JDK 7 and 8. */ public class ModuleReaderProxy implements Closeable { @@ -143,90 +143,7 @@ public InputStream open(final String path, final Runnable onClose) throws Securi if (inputStream == null) { throw new IllegalArgumentException("Got null result from ModuleReader#open(String)#get()"); } - // Return a proxying InputStream that will close the Resource when the InputStream is closed (#600) - return new InputStream() { - @Override - public void close() throws IOException { - if (onClose != null) { - try { - onClose.run(); - } catch (final Exception e) { - // Ignore - } - } - inputStream.close(); - } - - @Override - public int read() throws IOException { - return inputStream.read(); - } - - @Override - public int read(final byte[] b) throws IOException { - return inputStream.read(b); - } - - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - return inputStream.read(b, off, len); - } - - @Override - public byte[] readAllBytes() throws IOException { - return inputStream.readAllBytes(); - } - - @Override - public byte[] readNBytes(final int len) throws IOException { - return inputStream.readNBytes(len); - } - - @Override - public int readNBytes(final byte[] b, final int off, final int len) throws IOException { - return inputStream.readNBytes(b, off, len); - } - - @Override - public int available() throws IOException { - return inputStream.available(); - } - - @Override - public boolean markSupported() { - return inputStream.markSupported(); - } - - @Override - public synchronized void mark(final int readlimit) { - inputStream.mark(readlimit); - } - - @Override - public synchronized void reset() throws IOException { - inputStream.reset(); - } - - @Override - public long skip(final long n) throws IOException { - return inputStream.skip(n); - } - - @Override - public void skipNBytes(final long n) throws IOException { - inputStream.skipNBytes(n); - } - - @Override - public long transferTo(final OutputStream out) throws IOException { - return inputStream.transferTo(out); - } - - @Override - public String toString() { - return inputStream.toString(); - } - }; + return new InputStreamWithCloseAction(inputStream, onClose); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java new file mode 100644 index 000000000..b01f40331 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java @@ -0,0 +1,210 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; + +/** A proxying InputStream that will close the Resource when the InputStream is closed (#600). */ +public class InputStreamWithCloseAction extends InputStream { + private final InputStream inputStream; + private final Runnable onClose; + + private static Method readAllBytes; + private static Method readNBytes1; + private static Method readNBytes3; + private static Method skipNBytes; + private static Method transferTo; + + static { + // Use reflection for InputStream methods not present in JDK 7 + try { + readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes1 = InputStream.class.getDeclaredMethod("readNBytes", int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes3 = InputStream.class.getDeclaredMethod("readNBytes", byte[].class, int.class, int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + skipNBytes = InputStream.class.getDeclaredMethod("skipNBytes", long.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + transferTo = InputStream.class.getDeclaredMethod("transferTo", OutputStream.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + } + + /** + * A proxying InputStream that will close the Resource when the InputStream is closed (#600). + * + * @param inputStream + * the {@link InputStream} to wrap. + * @param onClose + * the action to run when {@link #close()} is called. + */ + public InputStreamWithCloseAction(final InputStream inputStream, final Runnable onClose) { + this.inputStream = inputStream; + this.onClose = onClose; + } + + @Override + public void close() throws IOException { + if (onClose != null) { + try { + onClose.run(); + } catch (final Exception e) { + // Ignore + } + } + inputStream.close(); + } + + @Override + public int read() throws IOException { + return inputStream.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + return inputStream.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + return inputStream.read(b, off, len); + } + + // No @Override, since this method is not present in JDK 7 + @Override + public byte[] readAllBytes() throws IOException { + if (readAllBytes == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readAllBytes.invoke(inputStream); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + @Override + public byte[] readNBytes(final int len) throws IOException { + if (readNBytes1 == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readNBytes1.invoke(inputStream, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + @Override + public int readNBytes(final byte[] b, final int off, final int len) throws IOException { + if (readNBytes3 == null) { + throw new UnsupportedOperationException(); + } + try { + return (int) readNBytes3.invoke(inputStream, b, off, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public int available() throws IOException { + return inputStream.available(); + } + + @Override + public boolean markSupported() { + return inputStream.markSupported(); + } + + @Override + public synchronized void mark(final int readlimit) { + inputStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + inputStream.reset(); + } + + @Override + public long skip(final long n) throws IOException { + return inputStream.skip(n); + } + + // No @Override, since this method is not present in JDK 7 + @Override + public void skipNBytes(final long n) throws IOException { + if (skipNBytes == null) { + throw new UnsupportedOperationException(); + } + try { + skipNBytes.invoke(inputStream, n); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + @Override + public long transferTo(final OutputStream out) throws IOException { + if (transferTo == null) { + throw new UnsupportedOperationException(); + } + try { + return (long) transferTo.invoke(inputStream, out); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public String toString() { + return inputStream.toString(); + } +} From dd66c9925d6d89fb5769fa4c6433b51dda295304 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:15:50 -0700 Subject: [PATCH 264/713] Update Javadoc --- .../github/classgraph/utils/InputStreamWithCloseAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java index b01f40331..94adebaf4 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java @@ -33,7 +33,7 @@ import java.io.OutputStream; import java.lang.reflect.Method; -/** A proxying InputStream that will close the Resource when the InputStream is closed (#600). */ +/** A proxying {@link InputStream} that runs a given action when {@link #close()} is called (#600). */ public class InputStreamWithCloseAction extends InputStream { private final InputStream inputStream; private final Runnable onClose; @@ -74,7 +74,7 @@ public class InputStreamWithCloseAction extends InputStream { } /** - * A proxying InputStream that will close the Resource when the InputStream is closed (#600). + * A proxying {@link InputStream} that runs a given action when {@link #close()} is called (#600). * * @param inputStream * the {@link InputStream} to wrap. From 585711ffa67683d43bae7502d6f92730abb5c0c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:20:10 -0700 Subject: [PATCH 265/713] Refactoring --- .../classgraph/ClasspathElementFileDir.java | 25 +++++++++---------- .../classgraph/ClasspathElementModule.java | 25 +++++++++---------- .../classgraph/ClasspathElementPathDir.java | 25 +++++++++---------- .../classgraph/ClasspathElementZip.java | 25 +++++++++---------- 4 files changed, 48 insertions(+), 52 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 2584e9345..1473bf1ec 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -178,6 +178,16 @@ private Resource newResource(final String pathRelativeToPackageRoot, final File /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); + /** Action to run when a derived resource is closed. */ + private final Runnable onClose = new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; + @Override public String getPath() { String path = FastPathResolver.resolve(pathRelativeToPackageRoot); @@ -246,7 +256,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new FileSlice and then open it fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); length = fileSlice.sliceLength; - return new ClassfileReader(fileSlice, onClose()); + return new ClassfileReader(fileSlice, onClose); } @Override @@ -260,7 +270,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - inputStream = fileSlice.open(onClose()); + inputStream = fileSlice.open(onClose); length = fileSlice.sliceLength; return inputStream; } @@ -291,17 +301,6 @@ public void close() { } super.close(); // Close inputStream } - - private Runnable onClose() { - return new Runnable() { - @Override - public void run() { - if (isOpen.get()) { - close(); - } - } - }; - } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index bc6e480b0..91e180e41 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -141,6 +141,16 @@ private Resource newResource(final String resourcePath) { /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); + + /** Action to run when a derived resource is closed. */ + private final Runnable onClose = new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; @Override public String getPath() { @@ -188,7 +198,7 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open(), onClose()); + return new ClassfileReader(open(), onClose); } @Override @@ -203,7 +213,7 @@ public InputStream open() throws IOException { } try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); - inputStream = moduleReaderProxy.open(resourcePath, onClose()); + inputStream = moduleReaderProxy.open(resourcePath, onClose); // Length cannot be obtained from ModuleReader length = -1L; return inputStream; @@ -248,17 +258,6 @@ public void close() { } super.close(); // Close inputStream } - - private Runnable onClose() { - return new Runnable() { - @Override - public void run() { - if (isOpen.get()) { - close(); - } - } - }; - } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 687876341..e9689272a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -183,6 +183,16 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); + /** Action to run when a derived resource is closed. */ + private final Runnable onClose = new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; + @Override public String getPath() { String path = FastPathResolver.resolve(packageRootPath.relativize(resourcePath).toString()); @@ -252,7 +262,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new PathSlice and then open it pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; - return new ClassfileReader(pathSlice, onClose()); + return new ClassfileReader(pathSlice, onClose); } @Override @@ -266,7 +276,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } pathSlice = new PathSlice(resourcePath, nestedJarHandler); - inputStream = pathSlice.open(onClose()); + inputStream = pathSlice.open(onClose); length = pathSlice.sliceLength; return inputStream; } @@ -297,17 +307,6 @@ public void close() { } super.close(); // Close inputStream } - - private Runnable onClose() { - return new Runnable() { - @Override - public void run() { - if (isOpen.get()) { - close(); - } - } - }; - } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index b11efb829..6944303be 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -318,6 +318,16 @@ private Resource newResource(final FastZipEntry zipEntry, final String pathRelat /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); + /** Action to run when a derived resource is closed. */ + private final Runnable onClose = new Runnable() { + @Override + public void run() { + if (isOpen.get()) { + close(); + } + } + }; + /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -391,7 +401,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { - inputStream = zipEntry.getSlice().open(onClose()); + inputStream = zipEntry.getSlice().open(onClose); length = zipEntry.uncompressedSize; return inputStream; @@ -403,7 +413,7 @@ public InputStream open() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open(), onClose()); + return new ClassfileReader(open(), onClose); } @Override @@ -452,17 +462,6 @@ public void close() { } super.close(); // Close inputStream } - - private Runnable onClose() { - return new Runnable() { - @Override - public void run() { - if (isOpen.get()) { - close(); - } - } - }; - } }; } From b550a10b775e4fe122c5667a3f31bb41eb43ee60 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:21:34 -0700 Subject: [PATCH 266/713] Add comment --- .../io/github/classgraph/utils/InputStreamWithCloseAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java index 94adebaf4..fb082b262 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java @@ -45,7 +45,8 @@ public class InputStreamWithCloseAction extends InputStream { private static Method transferTo; static { - // Use reflection for InputStream methods not present in JDK 7 + // Use reflection for InputStream methods not present in JDK 7. + // TODO Switch to direct method calls once JDK 8 is required. try { readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); } catch (NoSuchMethodException | SecurityException e1) { From 92fbb5f8fcf36821b655d760be6e76526aef8757 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:35:37 -0700 Subject: [PATCH 267/713] Add `CloseableByteBuffer Resource#readCloseable()` (#600) --- .../classgraph/ClasspathElementModule.java | 2 +- .../classgraph/CloseableByteBuffer.java | 71 +++++++++++++++++++ .../java/io/github/classgraph/Resource.java | 27 ++++++- .../utils/InputStreamWithCloseAction.java | 5 +- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/github/classgraph/CloseableByteBuffer.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 91e180e41..7723e518e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -141,7 +141,7 @@ private Resource newResource(final String resourcePath) { /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); - + /** Action to run when a derived resource is closed. */ private final Runnable onClose = new Runnable() { @Override diff --git a/src/main/java/io/github/classgraph/CloseableByteBuffer.java b/src/main/java/io/github/classgraph/CloseableByteBuffer.java new file mode 100644 index 000000000..7c826686b --- /dev/null +++ b/src/main/java/io/github/classgraph/CloseableByteBuffer.java @@ -0,0 +1,71 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.github.classgraph; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * A wrapper for {@link ByteBuffer} that implements the {@link Closeable} interface, releasing the + * {@link ByteBuffer} when it is no longer needed. + */ +public class CloseableByteBuffer implements Closeable { + private ByteBuffer byteBuffer; + private Runnable onClose; + + /** + * A wrapper for {@link ByteBuffer} that implements the {@link Closeable} interface, releasing the + * {@link ByteBuffer} when it is no longer needed. + */ + public CloseableByteBuffer(final ByteBuffer byteBuffer, final Runnable onClose) { + this.byteBuffer = byteBuffer; + this.onClose = onClose; + } + + /** + * @return The wrapped {@link ByteBuffer}. + */ + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + @Override + public void close() throws IOException { + if (onClose != null) { + try { + onClose.run(); + } catch (final Exception e) { + // Ignore + } + onClose = null; + } + byteBuffer = null; + } +} diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 4848660a9..2445acf13 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -253,14 +253,37 @@ public String getContentAsString() throws IOException { /** * Open a {@link ByteBuffer} for a classpath resource. Make sure you call {@link Resource#close()} when you are - * finished with the {@link ByteBuffer}, so that the {@link ByteBuffer} is released or unmapped. + * finished with the {@link ByteBuffer}, so that the {@link ByteBuffer} is released or unmapped. See also + * {@link #readCloseable()}. * * @return The allocated or mapped {@link ByteBuffer} for the resource file content. * @throws IOException - * If the resource could not be opened. + * If the resource could not be read. */ public abstract ByteBuffer read() throws IOException; + /** + * Open a {@link ByteBuffer} for a classpath resource, and wrap it in a {@link CloseableByteBuffer} instance, + * which implements the {@link Closeable#close()} method to free the underlying {@link ByteBuffer} when + * {@link CloseableByteBuffer#close()} is called, by automatically calling {@link Resource#close()}. + * + *

+ * Call {@link CloseableByteBuffer#getByteBuffer()} on the returned instance to access the underlying + * {@link ByteBuffer}. + * + * @return The allocated or mapped {@link ByteBuffer} for the resource file content. + * @throws IOException + * If the resource could not be read. + */ + public CloseableByteBuffer readCloseable() throws IOException { + return new CloseableByteBuffer(read(), new Runnable() { + @Override + public void run() { + close(); + } + }); + } + /** * Load a classpath resource and return its content as a byte array. Automatically calls * {@link Resource#close()} after loading the byte array and before returning it, so that the underlying diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java index fb082b262..3a7c844db 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java @@ -33,8 +33,9 @@ import java.io.OutputStream; import java.lang.reflect.Method; -/** A proxying {@link InputStream} that runs a given action when {@link #close()} is called (#600). */ +/** A proxying {@link InputStream} that runs a given action when {@link #close()} is called. */ public class InputStreamWithCloseAction extends InputStream { + // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. private final InputStream inputStream; private final Runnable onClose; @@ -75,7 +76,7 @@ public class InputStreamWithCloseAction extends InputStream { } /** - * A proxying {@link InputStream} that runs a given action when {@link #close()} is called (#600). + * A proxying {@link InputStream} that runs a given action when {@link #close()} is called. * * @param inputStream * the {@link InputStream} to wrap. From a244b2ca13eebd984c27c58a255c681b79291115 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:39:57 -0700 Subject: [PATCH 268/713] JDK7 compat fix --- .../classgraph/utils/InputStreamWithCloseAction.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java index 3a7c844db..04c42ef50 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java @@ -47,7 +47,7 @@ public class InputStreamWithCloseAction extends InputStream { static { // Use reflection for InputStream methods not present in JDK 7. - // TODO Switch to direct method calls once JDK 8 is required. + // TODO Switch to direct method calls once JDK 8 is required, and add back missing @Override annotations. try { readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); } catch (NoSuchMethodException | SecurityException e1) { @@ -116,7 +116,6 @@ public int read(final byte[] b, final int off, final int len) throws IOException } // No @Override, since this method is not present in JDK 7 - @Override public byte[] readAllBytes() throws IOException { if (readAllBytes == null) { throw new UnsupportedOperationException(); @@ -129,7 +128,6 @@ public byte[] readAllBytes() throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public byte[] readNBytes(final int len) throws IOException { if (readNBytes1 == null) { throw new UnsupportedOperationException(); @@ -142,7 +140,6 @@ public byte[] readNBytes(final int len) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public int readNBytes(final byte[] b, final int off, final int len) throws IOException { if (readNBytes3 == null) { throw new UnsupportedOperationException(); @@ -180,7 +177,6 @@ public long skip(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public void skipNBytes(final long n) throws IOException { if (skipNBytes == null) { throw new UnsupportedOperationException(); @@ -193,7 +189,6 @@ public void skipNBytes(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public long transferTo(final OutputStream out) throws IOException { if (transferTo == null) { throw new UnsupportedOperationException(); From 4efc2e1fac11a92d5729949345311b1cdc66bb79 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Nov 2021 15:40:18 -0700 Subject: [PATCH 269/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2048b8f9..c6dd0ea83 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.135-SNAPSHOT + 4.8.136-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 914fe85229481b551901ae3b74637cdcbce63963 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 02:01:40 +0000 Subject: [PATCH 270/713] Bump jvm-driver from 8.5.0 to 8.6.0 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.5.0 to 8.6.0. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.5.0...jvm-driver-8.6.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6dd0ea83..01d339ea3 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.5.0 + 8.6.0 true From 53f01dacd67ca7b190b3f67a46582a252039c7e0 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 22 Nov 2021 09:26:47 +0000 Subject: [PATCH 271/713] Only invoke Resource's super.close() if the resource is open. --- .../classgraph/ClasspathElementFileDir.java | 8 ++--- .../classgraph/ClasspathElementModule.java | 32 ++++++++++--------- .../classgraph/ClasspathElementPathDir.java | 8 ++--- .../classgraph/ClasspathElementZip.java | 18 ++++++----- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 1473bf1ec..7d220d6fc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -182,9 +182,7 @@ private Resource newResource(final String pathRelativeToPackageRoot, final File private final Runnable onClose = new Runnable() { @Override public void run() { - if (isOpen.get()) { - close(); - } + close(); } }; @@ -298,8 +296,10 @@ public void close() { nestedJarHandler.markSliceAsClosed(fileSlice); fileSlice = null; } + + // Close inputStream + super.close(); } - super.close(); // Close inputStream } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 7723e518e..ad4c4b7a6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -146,9 +146,7 @@ private Resource newResource(final String resourcePath) { private final Runnable onClose = new Runnable() { @Override public void run() { - if (isOpen.get()) { - close(); - } + close(); } }; @@ -243,20 +241,24 @@ public byte[] load() throws IOException { @Override public void close() { - if (isOpen.getAndSet(false) && moduleReaderProxy != null) { - if (byteBuffer != null) { - // Release any open ByteBuffer - moduleReaderProxy.release(byteBuffer); - byteBuffer = null; + if (isOpen.getAndSet(false)) { + if (moduleReaderProxy != null) { + if (byteBuffer != null) { + // Release any open ByteBuffer + moduleReaderProxy.release(byteBuffer); + byteBuffer = null; + } + // Recycle the (open) ModuleReaderProxy instance. + moduleReaderProxyRecycler.recycle(moduleReaderProxy); + // Don't call ModuleReaderProxy#close(), leave the ModuleReaderProxy open in the recycler. + // Just set the ref to null here. The ModuleReaderProxy will be closed by + // ClasspathElementModule#close(). + moduleReaderProxy = null; } - // Recycle the (open) ModuleReaderProxy instance. - moduleReaderProxyRecycler.recycle(moduleReaderProxy); - // Don't call ModuleReaderProxy#close(), leave the ModuleReaderProxy open in the recycler. - // Just set the ref to null here. The ModuleReaderProxy will be closed by - // ClasspathElementModule#close(). - moduleReaderProxy = null; + + // Close inputStream + super.close(); } - super.close(); // Close inputStream } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index e9689272a..a3ecb8f40 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -187,9 +187,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes private final Runnable onClose = new Runnable() { @Override public void run() { - if (isOpen.get()) { - close(); - } + close(); } }; @@ -304,8 +302,10 @@ public void close() { nestedJarHandler.markSliceAsClosed(pathSlice); pathSlice = null; } + + // Close inputStream + super.close(); } - super.close(); // Close inputStream } }; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 6944303be..8f68ddc45 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -322,9 +322,7 @@ private Resource newResource(final FastZipEntry zipEntry, final String pathRelat private final Runnable onClose = new Runnable() { @Override public void run() { - if (isOpen.get()) { - close(); - } + close(); } }; @@ -455,12 +453,16 @@ public byte[] load() throws IOException { @Override public void close() { - if (isOpen.getAndSet(false) && byteBuffer != null) { - // ByteBuffer should be a duplicate or slice, or should wrap an array, so it doesn't - // need to be unmapped - byteBuffer = null; + if (isOpen.getAndSet(false)) { + if (byteBuffer != null) { + // ByteBuffer should be a duplicate or slice, or should wrap an array, so it doesn't + // need to be unmapped + byteBuffer = null; + } + + // Close inputStream + super.close(); } - super.close(); // Close inputStream } }; } From 220be7bf111aeb67368bb34f56e739a9bec3a934 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 03:20:46 -0700 Subject: [PATCH 272/713] Close underlying Resource directly (#602) --- .../classgraph/ClasspathElementFileDir.java | 12 ++------ .../classgraph/ClasspathElementModule.java | 13 ++------- .../classgraph/ClasspathElementPathDir.java | 12 ++------ .../classgraph/ClasspathElementZip.java | 20 ++++--------- .../github/classgraph/ModuleReaderProxy.java | 11 ++++--- .../io/github/classgraph/fileslice/Slice.java | 17 +++++++---- .../fileslice/reader/ClassfileReader.java | 29 ++++++++++--------- ...tion.java => InputStreamFromResource.java} | 22 +++++++------- 8 files changed, 56 insertions(+), 80 deletions(-) rename src/main/java/nonapi/io/github/classgraph/utils/{InputStreamWithCloseAction.java => InputStreamFromResource.java} (90%) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 7d220d6fc..9b94e5fc9 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -178,14 +178,6 @@ private Resource newResource(final String pathRelativeToPackageRoot, final File /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); - /** Action to run when a derived resource is closed. */ - private final Runnable onClose = new Runnable() { - @Override - public void run() { - close(); - } - }; - @Override public String getPath() { String path = FastPathResolver.resolve(pathRelativeToPackageRoot); @@ -254,7 +246,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new FileSlice and then open it fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); length = fileSlice.sliceLength; - return new ClassfileReader(fileSlice, onClose); + return new ClassfileReader(fileSlice, this); } @Override @@ -268,7 +260,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - inputStream = fileSlice.open(onClose); + inputStream = fileSlice.open(this); length = fileSlice.sliceLength; return inputStream; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index ad4c4b7a6..af002ea92 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -52,6 +52,7 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.CollectionUtils; +import nonapi.io.github.classgraph.utils.InputStreamFromResource; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; @@ -142,14 +143,6 @@ private Resource newResource(final String resourcePath) { /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); - /** Action to run when a derived resource is closed. */ - private final Runnable onClose = new Runnable() { - @Override - public void run() { - close(); - } - }; - @Override public String getPath() { return resourcePath; @@ -196,7 +189,7 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open(), onClose); + return new ClassfileReader(open(), this); } @Override @@ -211,7 +204,7 @@ public InputStream open() throws IOException { } try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); - inputStream = moduleReaderProxy.open(resourcePath, onClose); + inputStream = new InputStreamFromResource(moduleReaderProxy.open(resourcePath), this); // Length cannot be obtained from ModuleReader length = -1L; return inputStream; diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index a3ecb8f40..5bc85ebc6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -183,14 +183,6 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); - /** Action to run when a derived resource is closed. */ - private final Runnable onClose = new Runnable() { - @Override - public void run() { - close(); - } - }; - @Override public String getPath() { String path = FastPathResolver.resolve(packageRootPath.relativize(resourcePath).toString()); @@ -260,7 +252,7 @@ ClassfileReader openClassfile() throws IOException { // Classfile won't be compressed, so wrap it in a new PathSlice and then open it pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; - return new ClassfileReader(pathSlice, onClose); + return new ClassfileReader(pathSlice, this); } @Override @@ -274,7 +266,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } pathSlice = new PathSlice(resourcePath, nestedJarHandler); - inputStream = pathSlice.open(onClose); + inputStream = pathSlice.open(this); length = pathSlice.sliceLength; return inputStream; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 8f68ddc45..1764c603b 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -318,14 +318,6 @@ private Resource newResource(final FastZipEntry zipEntry, final String pathRelat /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); - /** Action to run when a derived resource is closed. */ - private final Runnable onClose = new Runnable() { - @Override - public void run() { - close(); - } - }; - /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -388,6 +380,11 @@ public Set getPosixFilePermissions() { return perms; } + @Override + ClassfileReader openClassfile() throws IOException { + return new ClassfileReader(open(), this); + } + @Override public InputStream open() throws IOException { if (skipClasspathElement) { @@ -399,7 +396,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { - inputStream = zipEntry.getSlice().open(onClose); + inputStream = zipEntry.getSlice().open(this); length = zipEntry.uncompressedSize; return inputStream; @@ -409,11 +406,6 @@ public InputStream open() throws IOException { } } - @Override - ClassfileReader openClassfile() throws IOException { - return new ClassfileReader(open(), onClose); - } - @Override public ByteBuffer read() throws IOException { if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index e6ab2e991..bdda3c58f 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -35,7 +35,6 @@ import java.util.List; import nonapi.io.github.classgraph.reflection.ReflectionUtils; -import nonapi.io.github.classgraph.utils.InputStreamWithCloseAction; /** A ModuleReader proxy, written using reflection to preserve backwards compatibility with JDK 7 and 8. */ public class ModuleReaderProxy implements Closeable { @@ -125,25 +124,25 @@ public List list() throws SecurityException { * * @param path * The path to the resource to open. - * @param onClose - * a method to run when the returned {@code InputStream} is closed, or null if none. * * @return An {@link InputStream} for the content of the resource. * @throws SecurityException * If the module cannot be accessed. + * @throws IllegalArgumentException + * If the module cannot be accessed. */ - public InputStream open(final String path, final Runnable onClose) throws SecurityException { + public InputStream open(final String path) throws SecurityException { final Object /* Optional */ optionalInputStream = ReflectionUtils .invokeMethod(/* throwException = */ true, moduleReader, "open", String.class, path); if (optionalInputStream == null) { - throw new IllegalArgumentException("Got null result from ModuleReader#open(String)"); + throw new IllegalArgumentException("Got null result from ModuleReader#open for path " + path); } final InputStream inputStream = (InputStream) ReflectionUtils.invokeMethod(/* throwException = */ true, optionalInputStream, "get"); if (inputStream == null) { throw new IllegalArgumentException("Got null result from ModuleReader#open(String)#get()"); } - return new InputStreamWithCloseAction(inputStream, onClose); + return inputStream; } /** diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java index b4ea19bdc..42db66e6f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -37,6 +37,7 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicBoolean; +import io.github.classgraph.Resource; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.FileUtils; @@ -156,13 +157,13 @@ public InputStream open() throws IOException { /** * Open this {@link Slice} as an {@link InputStream}. * - * @param onClose - * a method to run when the returned {@code InputStream} is closed, or null if none. + * @param resource + * the {@link Resource} to close when the returned {@code InputStream} is closed, or null if none. * @return the input stream * @throws IOException * if an inflater cannot be created for this {@link Slice}. */ - public InputStream open(final Runnable onClose) throws IOException { + public InputStream open(final Resource resource) throws IOException { final InputStream rawInputStream = new InputStream() { RandomAccessReader randomAccessReader = randomAccessReader(); private long currOff; @@ -232,10 +233,14 @@ public boolean markSupported() { @Override public void close() { - closed.getAndSet(true); - if (onClose != null) { - onClose.run(); + if (resource != null) { + try { + resource.close(); + } catch (final Exception e) { + // Ignore + } } + closed.getAndSet(true); } }; return isDeflatedZipEntry ? nestedJarHandler.openInflaterInputStream(rawInputStream) : rawInputStream; diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java index 7c98ec6d2..051fdd124 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java @@ -38,6 +38,7 @@ import java.nio.ReadOnlyBufferException; import java.util.Arrays; +import io.github.classgraph.Resource; import nonapi.io.github.classgraph.fileslice.ArraySlice; import nonapi.io.github.classgraph.fileslice.FileSlice; import nonapi.io.github.classgraph.fileslice.Slice; @@ -50,6 +51,9 @@ * classfile format. */ public class ClassfileReader implements RandomAccessReader, SequentialReader, Closeable { + /** The underlying resource to close when {@link ClassfileReader#close()} is called. */ + private Resource resource; + /** If slice is deflated, a wrapper for {@link InflateInputStream}. */ private InputStream inflaterInputStream; @@ -73,9 +77,6 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl */ private int classfileLengthHint = -1; - /** The handler executed by {@link ClassfileReader#close()}. */ - private Runnable onClose; - /** * Initial buffer size. For most classfiles, only the first 16-64kb needs to be read (we don't read the * bytecodes). @@ -96,14 +97,14 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl * * @param slice * the {@link Slice} to read. - * @param onClose - * the handler executed by {@link ClassfileReader#close()}. + * @param resource + * the underlying resource to close when {@link ClassfileReader#close()} is called. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final Slice slice, final Runnable onClose) throws IOException { + public ClassfileReader(final Slice slice, final Resource resource) throws IOException { this.classfileLengthHint = (int) slice.sliceLength; - this.onClose = onClose; + this.resource = resource; if (slice.isDeflatedZipEntry) { // If this is a deflated slice, need to read from an InflaterInputStream to fill buffer inflaterInputStream = slice.open(); @@ -139,15 +140,15 @@ public ClassfileReader(final Slice slice, final Runnable onClose) throws IOExcep * * @param inputStream * the {@link InputStream} to read from. - * @param onClose - * the handler executed by {@link ClassfileReader#close()}. + * @param resource + * the underlying resource to close when {@link ClassfileReader#close()} is called. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final InputStream inputStream, final Runnable onClose) throws IOException { + public ClassfileReader(final InputStream inputStream, final Resource resource) throws IOException { inflaterInputStream = inputStream; arr = new byte[INITIAL_BUF_SIZE]; - this.onClose = onClose; + this.resource = resource; } /** @@ -453,9 +454,9 @@ public void close() { inflaterInputStream.close(); inflaterInputStream = null; } - if (onClose != null) { - onClose.run(); - onClose = null; + if (resource != null) { + resource.close(); + resource = null; } } catch (final Exception e) { // Ignore diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java similarity index 90% rename from src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java rename to src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java index 04c42ef50..d8a1098f2 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamWithCloseAction.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java @@ -33,11 +33,13 @@ import java.io.OutputStream; import java.lang.reflect.Method; -/** A proxying {@link InputStream} that runs a given action when {@link #close()} is called. */ -public class InputStreamWithCloseAction extends InputStream { +import io.github.classgraph.Resource; + +/** A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. */ +public class InputStreamFromResource extends InputStream { // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. private final InputStream inputStream; - private final Runnable onClose; + private final Resource resource; private static Method readAllBytes; private static Method readNBytes1; @@ -76,23 +78,23 @@ public class InputStreamWithCloseAction extends InputStream { } /** - * A proxying {@link InputStream} that runs a given action when {@link #close()} is called. + * A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. * * @param inputStream * the {@link InputStream} to wrap. - * @param onClose - * the action to run when {@link #close()} is called. + * @param resource + * the resource to close when {@link #close()} is called. */ - public InputStreamWithCloseAction(final InputStream inputStream, final Runnable onClose) { + public InputStreamFromResource(final InputStream inputStream, final Resource resource) { this.inputStream = inputStream; - this.onClose = onClose; + this.resource = resource; } @Override public void close() throws IOException { - if (onClose != null) { + if (resource != null) { try { - onClose.run(); + resource.close(); } catch (final Exception e) { // Ignore } From d4b321e5e08c2c36370c716864e7adc7aed92d5e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 03:25:14 -0700 Subject: [PATCH 273/713] Refactoring (#602) --- .../classgraph/ClasspathElementModule.java | 1 - .../java/io/github/classgraph/Resource.java | 177 +++++++++++++++ .../utils/InputStreamFromResource.java | 209 ------------------ 3 files changed, 177 insertions(+), 210 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index af002ea92..7cb546dbc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -52,7 +52,6 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.CollectionUtils; -import nonapi.io.github.classgraph.utils.InputStreamFromResource; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 2445acf13..898b5c560 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -32,6 +32,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -422,4 +424,179 @@ public void close() { inputStream = null; } } + + // ------------------------------------------------------------------------------------------------------------- + + /** A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. */ + static class InputStreamFromResource extends InputStream { + // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. + private final InputStream inputStream; + private final Resource resource; + + private static Method readAllBytes; + private static Method readNBytes1; + private static Method readNBytes3; + private static Method skipNBytes; + private static Method transferTo; + + static { + // Use reflection for InputStream methods not present in JDK 7. + // TODO Switch to direct method calls once JDK 8 is required, and add back missing @Override annotations + try { + readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes1 = InputStream.class.getDeclaredMethod("readNBytes", int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes3 = InputStream.class.getDeclaredMethod("readNBytes", byte[].class, int.class, int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + skipNBytes = InputStream.class.getDeclaredMethod("skipNBytes", long.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + transferTo = InputStream.class.getDeclaredMethod("transferTo", OutputStream.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + } + + /** + * A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. + * + * @param inputStream + * the {@link InputStream} to wrap. + * @param resource + * the resource to close when {@link #close()} is called. + */ + public InputStreamFromResource(final InputStream inputStream, final Resource resource) { + this.inputStream = inputStream; + this.resource = resource; + } + + @Override + public void close() throws IOException { + if (resource != null) { + try { + resource.close(); + } catch (final Exception e) { + // Ignore + } + } + inputStream.close(); + } + + @Override + public int read() throws IOException { + return inputStream.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + return inputStream.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + return inputStream.read(b, off, len); + } + + // No @Override, since this method is not present in JDK 7 + public byte[] readAllBytes() throws IOException { + if (readAllBytes == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readAllBytes.invoke(inputStream); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public byte[] readNBytes(final int len) throws IOException { + if (readNBytes1 == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readNBytes1.invoke(inputStream, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public int readNBytes(final byte[] b, final int off, final int len) throws IOException { + if (readNBytes3 == null) { + throw new UnsupportedOperationException(); + } + try { + return (int) readNBytes3.invoke(inputStream, b, off, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public int available() throws IOException { + return inputStream.available(); + } + + @Override + public boolean markSupported() { + return inputStream.markSupported(); + } + + @Override + public synchronized void mark(final int readlimit) { + inputStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + inputStream.reset(); + } + + @Override + public long skip(final long n) throws IOException { + return inputStream.skip(n); + } + + // No @Override, since this method is not present in JDK 7 + public void skipNBytes(final long n) throws IOException { + if (skipNBytes == null) { + throw new UnsupportedOperationException(); + } + try { + skipNBytes.invoke(inputStream, n); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public long transferTo(final OutputStream out) throws IOException { + if (transferTo == null) { + throw new UnsupportedOperationException(); + } + try { + return (long) transferTo.invoke(inputStream, out); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public String toString() { + return inputStream.toString(); + } + } } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java deleted file mode 100644 index d8a1098f2..000000000 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamFromResource.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Luke Hutchison - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO - * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ -package nonapi.io.github.classgraph.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; - -import io.github.classgraph.Resource; - -/** A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. */ -public class InputStreamFromResource extends InputStream { - // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. - private final InputStream inputStream; - private final Resource resource; - - private static Method readAllBytes; - private static Method readNBytes1; - private static Method readNBytes3; - private static Method skipNBytes; - private static Method transferTo; - - static { - // Use reflection for InputStream methods not present in JDK 7. - // TODO Switch to direct method calls once JDK 8 is required, and add back missing @Override annotations. - try { - readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - readNBytes1 = InputStream.class.getDeclaredMethod("readNBytes", int.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - readNBytes3 = InputStream.class.getDeclaredMethod("readNBytes", byte[].class, int.class, int.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - skipNBytes = InputStream.class.getDeclaredMethod("skipNBytes", long.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - transferTo = InputStream.class.getDeclaredMethod("transferTo", OutputStream.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - } - - /** - * A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. - * - * @param inputStream - * the {@link InputStream} to wrap. - * @param resource - * the resource to close when {@link #close()} is called. - */ - public InputStreamFromResource(final InputStream inputStream, final Resource resource) { - this.inputStream = inputStream; - this.resource = resource; - } - - @Override - public void close() throws IOException { - if (resource != null) { - try { - resource.close(); - } catch (final Exception e) { - // Ignore - } - } - inputStream.close(); - } - - @Override - public int read() throws IOException { - return inputStream.read(); - } - - @Override - public int read(final byte[] b) throws IOException { - return inputStream.read(b); - } - - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - return inputStream.read(b, off, len); - } - - // No @Override, since this method is not present in JDK 7 - public byte[] readAllBytes() throws IOException { - if (readAllBytes == null) { - throw new UnsupportedOperationException(); - } - try { - return (byte[]) readAllBytes.invoke(inputStream); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public byte[] readNBytes(final int len) throws IOException { - if (readNBytes1 == null) { - throw new UnsupportedOperationException(); - } - try { - return (byte[]) readNBytes1.invoke(inputStream, len); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public int readNBytes(final byte[] b, final int off, final int len) throws IOException { - if (readNBytes3 == null) { - throw new UnsupportedOperationException(); - } - try { - return (int) readNBytes3.invoke(inputStream, b, off, len); - } catch (final Exception e) { - throw new IOException(e); - } - } - - @Override - public int available() throws IOException { - return inputStream.available(); - } - - @Override - public boolean markSupported() { - return inputStream.markSupported(); - } - - @Override - public synchronized void mark(final int readlimit) { - inputStream.mark(readlimit); - } - - @Override - public synchronized void reset() throws IOException { - inputStream.reset(); - } - - @Override - public long skip(final long n) throws IOException { - return inputStream.skip(n); - } - - // No @Override, since this method is not present in JDK 7 - public void skipNBytes(final long n) throws IOException { - if (skipNBytes == null) { - throw new UnsupportedOperationException(); - } - try { - skipNBytes.invoke(inputStream, n); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public long transferTo(final OutputStream out) throws IOException { - if (transferTo == null) { - throw new UnsupportedOperationException(); - } - try { - return (long) transferTo.invoke(inputStream, out); - } catch (final Exception e) { - throw new IOException(e); - } - } - - @Override - public String toString() { - return inputStream.toString(); - } -} From 29e311675d43503cc6ceb70ce27f8cea6d85f64c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 03:36:12 -0700 Subject: [PATCH 274/713] Add TODO --- src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index d0b48cf31..838f9f888 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -40,6 +40,7 @@ class ObjectTypedValueWrapper extends ScanResultObject { // Parameter value is split into different fields by type, so that serialization and deserialization // works properly (can't properly serialize a field of Object type, since the concrete type is not + // TODO: remove this class once JSON serialization is removed /** Enum value. */ // stored in JSON). private AnnotationEnumValue annotationEnumValue; From 5e8e9fb65ab7f7d22cb3ab8d43ccc20bc2b2bc9c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 03:44:01 -0700 Subject: [PATCH 275/713] Change param names --- .../java/io/github/classgraph/Resource.java | 21 +++++++++------- .../io/github/classgraph/fileslice/Slice.java | 8 +++---- .../fileslice/reader/ClassfileReader.java | 24 +++++++++---------- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 898b5c560..91da200fd 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -430,8 +430,8 @@ public void close() { /** A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. */ static class InputStreamFromResource extends InputStream { // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. - private final InputStream inputStream; - private final Resource resource; + private InputStream inputStream; + private Resource resourceToClose; private static Method readAllBytes; private static Method readNBytes1; @@ -474,24 +474,29 @@ static class InputStreamFromResource extends InputStream { * * @param inputStream * the {@link InputStream} to wrap. - * @param resource + * @param resourceToClose * the resource to close when {@link #close()} is called. */ - public InputStreamFromResource(final InputStream inputStream, final Resource resource) { + public InputStreamFromResource(final InputStream inputStream, final Resource resourceToClose) { this.inputStream = inputStream; - this.resource = resource; + this.resourceToClose = resourceToClose; } @Override public void close() throws IOException { - if (resource != null) { + if (resourceToClose != null) { try { - resource.close(); + resourceToClose.close(); } catch (final Exception e) { // Ignore } + resourceToClose = null; + } + try { + inputStream.close(); + } finally { + inputStream = null; } - inputStream.close(); } @Override diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java index 42db66e6f..78c842198 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -157,13 +157,13 @@ public InputStream open() throws IOException { /** * Open this {@link Slice} as an {@link InputStream}. * - * @param resource + * @param resourceToClose * the {@link Resource} to close when the returned {@code InputStream} is closed, or null if none. * @return the input stream * @throws IOException * if an inflater cannot be created for this {@link Slice}. */ - public InputStream open(final Resource resource) throws IOException { + public InputStream open(final Resource resourceToClose) throws IOException { final InputStream rawInputStream = new InputStream() { RandomAccessReader randomAccessReader = randomAccessReader(); private long currOff; @@ -233,9 +233,9 @@ public boolean markSupported() { @Override public void close() { - if (resource != null) { + if (resourceToClose != null) { try { - resource.close(); + resourceToClose.close(); } catch (final Exception e) { // Ignore } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java index 051fdd124..e007169ce 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java @@ -52,7 +52,7 @@ */ public class ClassfileReader implements RandomAccessReader, SequentialReader, Closeable { /** The underlying resource to close when {@link ClassfileReader#close()} is called. */ - private Resource resource; + private Resource resourceToClose; /** If slice is deflated, a wrapper for {@link InflateInputStream}. */ private InputStream inflaterInputStream; @@ -97,14 +97,14 @@ public class ClassfileReader implements RandomAccessReader, SequentialReader, Cl * * @param slice * the {@link Slice} to read. - * @param resource - * the underlying resource to close when {@link ClassfileReader#close()} is called. + * @param resourceToClose + * the resource to close when {@link ClassfileReader#close()} is called, or null. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final Slice slice, final Resource resource) throws IOException { + public ClassfileReader(final Slice slice, final Resource resourceToClose) throws IOException { this.classfileLengthHint = (int) slice.sliceLength; - this.resource = resource; + this.resourceToClose = resourceToClose; if (slice.isDeflatedZipEntry) { // If this is a deflated slice, need to read from an InflaterInputStream to fill buffer inflaterInputStream = slice.open(); @@ -140,15 +140,15 @@ public ClassfileReader(final Slice slice, final Resource resource) throws IOExce * * @param inputStream * the {@link InputStream} to read from. - * @param resource - * the underlying resource to close when {@link ClassfileReader#close()} is called. + * @param resourceToClose + * the underlying resource to close when {@link ClassfileReader#close()} is called, or null. * @throws IOException * If an inflater cannot be opened on the {@link Slice}. */ - public ClassfileReader(final InputStream inputStream, final Resource resource) throws IOException { + public ClassfileReader(final InputStream inputStream, final Resource resourceToClose) throws IOException { inflaterInputStream = inputStream; arr = new byte[INITIAL_BUF_SIZE]; - this.resource = resource; + this.resourceToClose = resourceToClose; } /** @@ -454,9 +454,9 @@ public void close() { inflaterInputStream.close(); inflaterInputStream = null; } - if (resource != null) { - resource.close(); - resource = null; + if (resourceToClose != null) { + resourceToClose.close(); + resourceToClose = null; } } catch (final Exception e) { // Ignore From 722fe7898774e86b659052929f3701ba26ca4f06 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 03:56:57 -0700 Subject: [PATCH 276/713] Refactoring (#602) --- .../classgraph/ClasspathElementModule.java | 16 +- .../java/io/github/classgraph/Resource.java | 182 ---------------- .../classgraph/utils/ProxyingInputStream.java | 203 ++++++++++++++++++ 3 files changed, 218 insertions(+), 183 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 7cb546dbc..ef9ea69f8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -53,6 +53,7 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.ProxyingInputStream; import nonapi.io.github.classgraph.utils.VersionFinder; /** @@ -202,8 +203,21 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { + final Resource resourceToClose = this; moduleReaderProxy = moduleReaderProxyRecycler.acquire(); - inputStream = new InputStreamFromResource(moduleReaderProxy.open(resourcePath), this); + inputStream = new ProxyingInputStream(moduleReaderProxy.open(resourcePath)) { + @Override + public void close() throws IOException { + // Close the wrapped InputStream obtained from moduleReaderProxy + super.close(); + try { + // Close the Resource + resourceToClose.close(); + } catch (final Exception e) { + // Ignore + } + } + }; // Length cannot be obtained from ModuleReader length = -1L; return inputStream; diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 91da200fd..2445acf13 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -32,8 +32,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -424,184 +422,4 @@ public void close() { inputStream = null; } } - - // ------------------------------------------------------------------------------------------------------------- - - /** A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. */ - static class InputStreamFromResource extends InputStream { - // See #600, this is used to close the underlying Resource when the Resource's InputStream is closed. - private InputStream inputStream; - private Resource resourceToClose; - - private static Method readAllBytes; - private static Method readNBytes1; - private static Method readNBytes3; - private static Method skipNBytes; - private static Method transferTo; - - static { - // Use reflection for InputStream methods not present in JDK 7. - // TODO Switch to direct method calls once JDK 8 is required, and add back missing @Override annotations - try { - readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - readNBytes1 = InputStream.class.getDeclaredMethod("readNBytes", int.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - readNBytes3 = InputStream.class.getDeclaredMethod("readNBytes", byte[].class, int.class, int.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - skipNBytes = InputStream.class.getDeclaredMethod("skipNBytes", long.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - try { - transferTo = InputStream.class.getDeclaredMethod("transferTo", OutputStream.class); - } catch (NoSuchMethodException | SecurityException e1) { - // Ignore - } - } - - /** - * A proxying {@link InputStream} that closes a given {@link Resource} when {@link #close()} is called. - * - * @param inputStream - * the {@link InputStream} to wrap. - * @param resourceToClose - * the resource to close when {@link #close()} is called. - */ - public InputStreamFromResource(final InputStream inputStream, final Resource resourceToClose) { - this.inputStream = inputStream; - this.resourceToClose = resourceToClose; - } - - @Override - public void close() throws IOException { - if (resourceToClose != null) { - try { - resourceToClose.close(); - } catch (final Exception e) { - // Ignore - } - resourceToClose = null; - } - try { - inputStream.close(); - } finally { - inputStream = null; - } - } - - @Override - public int read() throws IOException { - return inputStream.read(); - } - - @Override - public int read(final byte[] b) throws IOException { - return inputStream.read(b); - } - - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - return inputStream.read(b, off, len); - } - - // No @Override, since this method is not present in JDK 7 - public byte[] readAllBytes() throws IOException { - if (readAllBytes == null) { - throw new UnsupportedOperationException(); - } - try { - return (byte[]) readAllBytes.invoke(inputStream); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public byte[] readNBytes(final int len) throws IOException { - if (readNBytes1 == null) { - throw new UnsupportedOperationException(); - } - try { - return (byte[]) readNBytes1.invoke(inputStream, len); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public int readNBytes(final byte[] b, final int off, final int len) throws IOException { - if (readNBytes3 == null) { - throw new UnsupportedOperationException(); - } - try { - return (int) readNBytes3.invoke(inputStream, b, off, len); - } catch (final Exception e) { - throw new IOException(e); - } - } - - @Override - public int available() throws IOException { - return inputStream.available(); - } - - @Override - public boolean markSupported() { - return inputStream.markSupported(); - } - - @Override - public synchronized void mark(final int readlimit) { - inputStream.mark(readlimit); - } - - @Override - public synchronized void reset() throws IOException { - inputStream.reset(); - } - - @Override - public long skip(final long n) throws IOException { - return inputStream.skip(n); - } - - // No @Override, since this method is not present in JDK 7 - public void skipNBytes(final long n) throws IOException { - if (skipNBytes == null) { - throw new UnsupportedOperationException(); - } - try { - skipNBytes.invoke(inputStream, n); - } catch (final Exception e) { - throw new IOException(e); - } - } - - // No @Override, since this method is not present in JDK 7 - public long transferTo(final OutputStream out) throws IOException { - if (transferTo == null) { - throw new UnsupportedOperationException(); - } - try { - return (long) transferTo.invoke(inputStream, out); - } catch (final Exception e) { - throw new IOException(e); - } - } - - @Override - public String toString() { - return inputStream.toString(); - } - } } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java new file mode 100644 index 000000000..2d9c116d9 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java @@ -0,0 +1,203 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package nonapi.io.github.classgraph.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; + +/** + * A proxying {@link InputStream} implementation that compiles for JDK 7 but can support the methods added in JDK 8 + * by reflection. + */ +public class ProxyingInputStream extends InputStream { + private InputStream inputStream; + + private static Method readAllBytes; + private static Method readNBytes1; + private static Method readNBytes3; + private static Method skipNBytes; + private static Method transferTo; + + static { + // Use reflection for InputStream methods not present in JDK 7. + // TODO Switch to direct method calls once JDK 8 is required, and add back missing @Override annotations + try { + readAllBytes = InputStream.class.getDeclaredMethod("readAllBytes"); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes1 = InputStream.class.getDeclaredMethod("readNBytes", int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + readNBytes3 = InputStream.class.getDeclaredMethod("readNBytes", byte[].class, int.class, int.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + skipNBytes = InputStream.class.getDeclaredMethod("skipNBytes", long.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + try { + transferTo = InputStream.class.getDeclaredMethod("transferTo", OutputStream.class); + } catch (NoSuchMethodException | SecurityException e1) { + // Ignore + } + } + + /** + * A proxying {@link InputStream} implementation that compiles for JDK 7 but can support the methods added in + * JDK 8 by reflection. + * + * @param inputStream + * the {@link InputStream} to wrap. + */ + public ProxyingInputStream(final InputStream inputStream) { + this.inputStream = inputStream; + } + + @Override + public int read() throws IOException { + return inputStream.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + return inputStream.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + return inputStream.read(b, off, len); + } + + // No @Override, since this method is not present in JDK 7 + public byte[] readAllBytes() throws IOException { + if (readAllBytes == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readAllBytes.invoke(inputStream); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public byte[] readNBytes(final int len) throws IOException { + if (readNBytes1 == null) { + throw new UnsupportedOperationException(); + } + try { + return (byte[]) readNBytes1.invoke(inputStream, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public int readNBytes(final byte[] b, final int off, final int len) throws IOException { + if (readNBytes3 == null) { + throw new UnsupportedOperationException(); + } + try { + return (int) readNBytes3.invoke(inputStream, b, off, len); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public int available() throws IOException { + return inputStream.available(); + } + + @Override + public boolean markSupported() { + return inputStream.markSupported(); + } + + @Override + public synchronized void mark(final int readlimit) { + inputStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + inputStream.reset(); + } + + @Override + public long skip(final long n) throws IOException { + return inputStream.skip(n); + } + + // No @Override, since this method is not present in JDK 7 + public void skipNBytes(final long n) throws IOException { + if (skipNBytes == null) { + throw new UnsupportedOperationException(); + } + try { + skipNBytes.invoke(inputStream, n); + } catch (final Exception e) { + throw new IOException(e); + } + } + + // No @Override, since this method is not present in JDK 7 + public long transferTo(final OutputStream out) throws IOException { + if (transferTo == null) { + throw new UnsupportedOperationException(); + } + try { + return (long) transferTo.invoke(inputStream, out); + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public String toString() { + return inputStream.toString(); + } + + @Override + public void close() throws IOException { + try { + inputStream.close(); + } finally { + inputStream = null; + } + } +} From 86a5c454818520a2c968e51012752f4fb5988232 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:16:01 -0700 Subject: [PATCH 277/713] Change var name --- .../java/io/github/classgraph/ClasspathElementModule.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index ef9ea69f8..28c77aea6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -203,7 +203,7 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { - final Resource resourceToClose = this; + final Resource thisResource = this; moduleReaderProxy = moduleReaderProxyRecycler.acquire(); inputStream = new ProxyingInputStream(moduleReaderProxy.open(resourcePath)) { @Override @@ -211,8 +211,9 @@ public void close() throws IOException { // Close the wrapped InputStream obtained from moduleReaderProxy super.close(); try { - // Close the Resource - resourceToClose.close(); + // Close the Resource, releasing any underlying ByteBuffer and recycling + // the moduleReaderProxy + thisResource.close(); } catch (final Exception e) { // Ignore } From 14f42e2fced20c239df763290a6bc01f5dbca5a8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:26:22 -0700 Subject: [PATCH 278/713] Bump version number back down (#602) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01d339ea3..fd0419b9d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.136-SNAPSHOT + 4.8.135-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 963c3636ca5a549396d688391f336808f1857114 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:27:10 -0700 Subject: [PATCH 279/713] [maven-release-plugin] prepare release classgraph-4.8.135 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fd0419b9d..ba9268796 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.135-SNAPSHOT + 4.8.135 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.135 From 4f7d046819c731355d4c4cbfdf0b7eacc31bcc81 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:27:13 -0700 Subject: [PATCH 280/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ba9268796..01d339ea3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.135 + 4.8.136-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.135 + HEAD From e64e9efe076e88de899a6215590ecbbfe96026f2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:30:46 -0700 Subject: [PATCH 281/713] Make constructor non-public --- .../java/io/github/classgraph/CloseableByteBuffer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/CloseableByteBuffer.java b/src/main/java/io/github/classgraph/CloseableByteBuffer.java index 7c826686b..17cdc13a4 100644 --- a/src/main/java/io/github/classgraph/CloseableByteBuffer.java +++ b/src/main/java/io/github/classgraph/CloseableByteBuffer.java @@ -43,8 +43,13 @@ public class CloseableByteBuffer implements Closeable { /** * A wrapper for {@link ByteBuffer} that implements the {@link Closeable} interface, releasing the * {@link ByteBuffer} when it is no longer needed. + * + * @param byteBuffer + * The {@link ByteBuffer} to wrap + * @param onClose + * The method to run when {@link #close()} is called. */ - public CloseableByteBuffer(final ByteBuffer byteBuffer, final Runnable onClose) { + CloseableByteBuffer(final ByteBuffer byteBuffer, final Runnable onClose) { this.byteBuffer = byteBuffer; this.onClose = onClose; } From 9e61b12e0e66326784efb9207c1ad0ff1d81e900 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Nov 2021 04:34:14 -0700 Subject: [PATCH 282/713] Update Javadoc --- src/main/java/io/github/classgraph/CloseableByteBuffer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/classgraph/CloseableByteBuffer.java b/src/main/java/io/github/classgraph/CloseableByteBuffer.java index 17cdc13a4..3762ca9aa 100644 --- a/src/main/java/io/github/classgraph/CloseableByteBuffer.java +++ b/src/main/java/io/github/classgraph/CloseableByteBuffer.java @@ -61,6 +61,7 @@ public ByteBuffer getByteBuffer() { return byteBuffer; } + /** Release the wrapped {@link ByteBuffer}. */ @Override public void close() throws IOException { if (onClose != null) { From b36113988b95d2b55aa5c112dbe1098e20bd2288 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Nov 2021 02:01:42 +0000 Subject: [PATCH 283/713] Bump jvm-driver from 8.6.0 to 8.7.0 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.6.0 to 8.7.0. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.6.0...jvm-driver-8.7.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01d339ea3..8afe70106 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.6.0 + 8.7.0 true From 835d4d777247efd5fd196c858e3e6cb1cb60700a Mon Sep 17 00:00:00 2001 From: tak Date: Thu, 25 Nov 2021 13:41:48 +0100 Subject: [PATCH 284/713] Added missing getters for access flags/modifiers (ClassInfo, MethodInfo, FieldInfo) ClassInfo: Added isPrivate, isProtected getters for inner classes (see: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.7.6) MethdInfo: Added isPrivate, isProtected, isAbstract, isStrict getters (see: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.6) FieldInfo: Added isPrivate, isProtected ,isSynthetic, isEnum getters (https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.5) --- .../java/io/github/classgraph/ClassInfo.java | 24 +++++++++++-- .../java/io/github/classgraph/FieldInfo.java | 36 +++++++++++++++++++ .../java/io/github/classgraph/MethodInfo.java | 36 +++++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index c546c46a8..d957a778e 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1187,7 +1187,25 @@ public String getModifiersStr() { * @return true if this class is a public class. */ public boolean isPublic() { - return (modifiers & Modifier.PUBLIC) != 0; + return Modifier.isPublic(modifiers); + } + + /** + * Checks if the class is private. + * + * @return true if this class is a private class. + */ + public boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + /** + * Checks if the class is protected. + * + * @return true if this class is a protected class. + */ + public boolean isProtected() { + return Modifier.isProtected(modifiers); } /** @@ -1196,7 +1214,7 @@ public boolean isPublic() { * @return true if this class is an abstract class. */ public boolean isAbstract() { - return (modifiers & 0x400) != 0; + return Modifier.isAbstract(modifiers); } /** @@ -1214,7 +1232,7 @@ public boolean isSynthetic() { * @return true if this class is a final class. */ public boolean isFinal() { - return (modifiers & Modifier.FINAL) != 0; + return Modifier.isFinal(modifiers); } /** diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index dc6f339cb..cd8be5f35 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -180,6 +180,24 @@ public String getModifiersStr() { public boolean isPublic() { return Modifier.isPublic(modifiers); } + + /** + * Returns true if this field is private. + * + * @return True if the field is private. + */ + public boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + /** + * Returns true if this field is protected. + * + * @return True if the field is protected. + */ + public boolean isProtected() { + return Modifier.isProtected(modifiers); + } /** * Returns true if this field is static. @@ -207,6 +225,24 @@ public boolean isFinal() { public boolean isTransient() { return Modifier.isTransient(modifiers); } + + /** + * Returns true if this field is synthetic. + * + * @return True if the field is synthetic. + */ + public boolean isSynthetic() { + return (modifiers & 0x1000) != 0; + } + + /** + * Returns true if this field is an enum constant. + * + * @return True if the field is an enum constant. + */ + public boolean isEnum() { + return (modifiers & 0x4000) != 0; + } /** * Returns the modifier bits for the field. diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 7f2fff1ff..72740cb0b 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -339,6 +339,24 @@ public boolean isConstructor() { public boolean isPublic() { return Modifier.isPublic(modifiers); } + + /** + * Returns true if this method is private. + * + * @return True if this method is private. + */ + public boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + /** + * Returns true if this method is protected. + * + * @return True if this method is protected. + */ + public boolean isProtected() { + return Modifier.isProtected(modifiers); + } /** * Returns true if this method is static. @@ -402,6 +420,24 @@ public boolean isVarArgs() { public boolean isNative() { return Modifier.isNative(modifiers); } + + /** + * Returns true if this method is abstract. + * + * @return True if this method is abstract. + */ + public boolean isAbstract() { + return Modifier.isAbstract(modifiers); + } + + /** + * Returns true if this method is strict. + * + * @return True if this method is strict. + */ + public boolean isStrict() { + return Modifier.isStrict(modifiers); + } /** * Returns true if this method has a body (i.e. has an implementation in the containing class). From 00236165b4b29f628a9584e3e09a951e61f9bb10 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 01:11:57 -0700 Subject: [PATCH 285/713] [maven-release-plugin] prepare release classgraph-4.8.136 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 01d339ea3..aa07bcc06 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.136-SNAPSHOT + 4.8.136 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.136 From 770ef9338f51d0a71d2140efd9017cec5b2983c7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 01:12:00 -0700 Subject: [PATCH 286/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aa07bcc06..81a12aff6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.136 + 4.8.137-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.136 + HEAD From 7a66f12ae6f5111f191de7b30c0909d0e63df28a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 01:22:54 -0700 Subject: [PATCH 287/713] Source > Cleanup --- src/main/java/io/github/classgraph/ClassInfo.java | 4 ++-- src/main/java/io/github/classgraph/FieldInfo.java | 8 ++++---- src/main/java/io/github/classgraph/MethodInfo.java | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index d957a778e..b42923e54 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1189,7 +1189,7 @@ public String getModifiersStr() { public boolean isPublic() { return Modifier.isPublic(modifiers); } - + /** * Checks if the class is private. * @@ -1198,7 +1198,7 @@ public boolean isPublic() { public boolean isPrivate() { return Modifier.isPrivate(modifiers); } - + /** * Checks if the class is protected. * diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index cd8be5f35..de161edbb 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -180,7 +180,7 @@ public String getModifiersStr() { public boolean isPublic() { return Modifier.isPublic(modifiers); } - + /** * Returns true if this field is private. * @@ -189,7 +189,7 @@ public boolean isPublic() { public boolean isPrivate() { return Modifier.isPrivate(modifiers); } - + /** * Returns true if this field is protected. * @@ -225,7 +225,7 @@ public boolean isFinal() { public boolean isTransient() { return Modifier.isTransient(modifiers); } - + /** * Returns true if this field is synthetic. * @@ -234,7 +234,7 @@ public boolean isTransient() { public boolean isSynthetic() { return (modifiers & 0x1000) != 0; } - + /** * Returns true if this field is an enum constant. * diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 72740cb0b..410f0f06c 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -339,7 +339,7 @@ public boolean isConstructor() { public boolean isPublic() { return Modifier.isPublic(modifiers); } - + /** * Returns true if this method is private. * @@ -348,7 +348,7 @@ public boolean isPublic() { public boolean isPrivate() { return Modifier.isPrivate(modifiers); } - + /** * Returns true if this method is protected. * @@ -420,7 +420,7 @@ public boolean isVarArgs() { public boolean isNative() { return Modifier.isNative(modifiers); } - + /** * Returns true if this method is abstract. * @@ -429,7 +429,7 @@ public boolean isNative() { public boolean isAbstract() { return Modifier.isAbstract(modifiers); } - + /** * Returns true if this method is strict. * From c946fa92a5f210fd658d07626d059a9e57136690 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 09:56:38 -0700 Subject: [PATCH 288/713] Only call getRuntimeMXBean if needed (#605) --- .../java/io/github/classgraph/ClassGraph.java | 1 + .../io/github/classgraph/ModulePathInfo.java | 66 +++++++++++-------- .../java/io/github/classgraph/ScanResult.java | 1 + 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 06bc6414f..a11e6371b 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1764,6 +1764,7 @@ public List getModules() { * @return The {@link ModulePathInfo}. */ public ModulePathInfo getModulePathInfo() { + scanSpec.modulePathInfo.getRuntimeInfo(); return scanSpec.modulePathInfo; } } diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 341cb74a4..a9ff93c0c 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -33,6 +33,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.JarUtils; @@ -126,36 +127,43 @@ public class ModulePathInfo { '\0' // --add-reads (only one param per switch) ); + /** Set to true once {@link #getRuntimeInfo()} is called. */ + private final AtomicBoolean gotRuntimeInfo = new AtomicBoolean(); + /** Construct a {@link ModulePathInfo}. */ - public ModulePathInfo() { - // 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 - .classForNameOrNull("java.lang.management.ManagementFactory"); - final Object runtimeMXBean = managementFactory == null ? null - : ReflectionUtils.invokeStaticMethod(/* throwException = */ false, managementFactory, - "getRuntimeMXBean"); - @SuppressWarnings("unchecked") - final List commandlineArguments = runtimeMXBean == null ? null - : (List) ReflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean, - "getInputArguments"); - if (commandlineArguments != null) { - for (final String arg : commandlineArguments) { - for (int i = 0; i < fields.size(); i++) { - final String argSwitch = argSwitches.get(i); - if (arg.startsWith(argSwitch)) { - final String argParam = arg.substring(argSwitch.length()); - final Set argField = fields.get(i); - final char sepChar = argPartSeparatorChars.get(i); - if (sepChar == '\0') { - // Only one param per switch - argField.add(argParam); - } else { - // Split arg param into parts - argField.addAll(Arrays - .asList(JarUtils.smartPathSplit(argParam, sepChar, /* scanSpec = */ null))); + void getRuntimeInfo() { + // 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 + .classForNameOrNull("java.lang.management.ManagementFactory"); + final Object runtimeMXBean = managementFactory == null ? null + : ReflectionUtils.invokeStaticMethod(/* throwException = */ false, managementFactory, + "getRuntimeMXBean"); + @SuppressWarnings("unchecked") + final List commandlineArguments = runtimeMXBean == null ? null + : (List) ReflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean, + "getInputArguments"); + if (commandlineArguments != null) { + for (final String arg : commandlineArguments) { + for (int i = 0; i < fields.size(); i++) { + final String argSwitch = argSwitches.get(i); + if (arg.startsWith(argSwitch)) { + final String argParam = arg.substring(argSwitch.length()); + final Set argField = fields.get(i); + final char sepChar = argPartSeparatorChars.get(i); + if (sepChar == '\0') { + // Only one param per switch + argField.add(argParam); + } else { + // Split arg param into parts + argField.addAll(Arrays + .asList(JarUtils.smartPathSplit(argParam, sepChar, /* scanSpec = */ null))); + } } } } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b31432558..00cc9334f 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -449,6 +449,7 @@ public List getModules() { * @return The {@link ModulePathInfo}. */ public ModulePathInfo getModulePathInfo() { + scanSpec.modulePathInfo.getRuntimeInfo(); return scanSpec.modulePathInfo; } From 2b33e72c6d0d8baa55be43b99c6c2bb58dd95550 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 09:57:17 -0700 Subject: [PATCH 289/713] [maven-release-plugin] prepare release classgraph-4.8.137 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 68685b524..734369753 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.137-SNAPSHOT + 4.8.137 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.137 From 874442f4a1c76b2bd63284988d20c8776e0b0310 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 09:57:19 -0700 Subject: [PATCH 290/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 734369753..486f87d0d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.137 + 4.8.138-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.137 + HEAD From 52db73f29bf98a81628f798dcc4744b399bdff6b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Nov 2021 10:03:37 -0700 Subject: [PATCH 291/713] Update JavaDoc --- src/main/java/io/github/classgraph/ModulePathInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index a9ff93c0c..998f62710 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -130,7 +130,7 @@ public class ModulePathInfo { /** Set to true once {@link #getRuntimeInfo()} is called. */ private final AtomicBoolean gotRuntimeInfo = new AtomicBoolean(); - /** Construct a {@link ModulePathInfo}. */ + /** Fill in module info from VM commandline parameters. */ void getRuntimeInfo() { // Only call this reflective method if ModulePathInfo is specifically requested, to avoid illegal // access warning on some JREs, e.g. Adopt JDK 11 (#605) From cfed886f9f75d37cb3399888ec5de78fb856ac2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 02:01:37 +0000 Subject: [PATCH 292/713] Bump junit-jupiter from 5.8.1 to 5.8.2 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.8.1 to 5.8.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.1...r5.8.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 486f87d0d..38bafe676 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ org.junit.jupiter junit-jupiter - 5.8.1 + 5.8.2 test From ce52201e3897c608ce49d1362ca1803b3fdaff02 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 6 Dec 2021 11:40:23 -0700 Subject: [PATCH 293/713] Add getEnumConstants() method --- .../java/io/github/classgraph/ClassInfo.java | 14 +++++++ .../github/classgraph/features/EnumTest.java | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/test/java/io/github/classgraph/features/EnumTest.java diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index b42923e54..c08afddba 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -51,6 +51,7 @@ import io.github.classgraph.Classfile.ClassContainment; import io.github.classgraph.Classfile.ClassTypeAnnotationDecorator; +import io.github.classgraph.FieldInfoList.FieldInfoFilter; import nonapi.io.github.classgraph.json.Id; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; @@ -2666,6 +2667,19 @@ public FieldInfoList getFieldInfo() { return fieldInfoList; } + /** Get all enum constants of an enum class (as a list of {@link FieldInfo} objects). */ + public FieldInfoList getEnumConstants() { + if (!isEnum()) { + throw new IllegalArgumentException("Class " + getName() + " is not an enum"); + } + return getFieldInfo().filter(new FieldInfoFilter() { + @Override + public boolean accept(FieldInfo fieldInfo) { + return fieldInfo.isEnum(); + } + }) + } + /** * Returns information on the named field declared by the class, but not by its superclasses. See also: * diff --git a/src/test/java/io/github/classgraph/features/EnumTest.java b/src/test/java/io/github/classgraph/features/EnumTest.java new file mode 100644 index 000000000..0ee14528c --- /dev/null +++ b/src/test/java/io/github/classgraph/features/EnumTest.java @@ -0,0 +1,39 @@ +package io.github.classgraph.features; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.FieldInfo; +import io.github.classgraph.ScanResult; + +/** + * Test. + */ +public class EnumTest { + /** Enum */ + private static enum MyEnum { + A, B, C; + } + + /** + * Test records (JDK 14+). + * + * @throws Exception + * the exception + */ + @Test + public void recordJar() throws Exception { + try (ScanResult scanResult = new ClassGraph().acceptClasses(MyEnum.class.getName()).enableAllInfo() + .scan()) { + assertThat(scanResult.getAllEnums().size() == 1); + final ClassInfo myEnum = scanResult.getAllEnums().get(0); + assertThat(myEnum.getName().equals(MyEnum.class.getName())); + for (FieldInfo fi : myEnum.getFieldInfo()) { + System.out.println(fi); + } + } + } +} From acb89996b6e1aa5746cec65cfcbe93b22222b8c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Dec 2021 02:01:42 +0000 Subject: [PATCH 294/713] Bump jvm-driver from 8.7.0 to 8.7.5 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.7.0 to 8.7.5. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.7.0...jvm-driver-8.7.5) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38bafe676..e0d9bc2c1 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.0 + 8.7.5 true From f9eaae790175c3a2c2a176184cc78d0f630a1606 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 Dec 2021 10:12:30 -0700 Subject: [PATCH 295/713] Mitigate log4j2 vulnerability CVE-2021-44228, if log4j is the logger --- .../java/nonapi/io/github/classgraph/utils/LogNode.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java index 361f67c57..7b686e096 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java @@ -49,6 +49,12 @@ * retain a sane order. The order may also be made deterministic by specifying a sort key for log entries. */ public final class LogNode { + // Mitigate log4j2 vulnerability (CVE-2021-44228), in case log4j is added to the classpath as the logger + // https://blog.cloudflare.com/inside-the-log4j2-vulnerability-cve-2021-44228/ + static { + System.getProperties().setProperty("log4j2.formatMsgNoLookups", "true"); + } + /** The logger. */ private static final Logger log = Logger.getLogger(ClassGraph.class.getName()); From 0ee47aa7b0f6f4cc6d414bb8db52bce97ef7fdb5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 Dec 2021 10:14:48 -0700 Subject: [PATCH 296/713] Fix prev checkin --- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- src/test/java/io/github/classgraph/features/EnumTest.java | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index c08afddba..7da5602cc 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2677,7 +2677,7 @@ public FieldInfoList getEnumConstants() { public boolean accept(FieldInfo fieldInfo) { return fieldInfo.isEnum(); } - }) + }); } /** diff --git a/src/test/java/io/github/classgraph/features/EnumTest.java b/src/test/java/io/github/classgraph/features/EnumTest.java index 0ee14528c..4d83c6b8c 100644 --- a/src/test/java/io/github/classgraph/features/EnumTest.java +++ b/src/test/java/io/github/classgraph/features/EnumTest.java @@ -6,7 +6,6 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; -import io.github.classgraph.FieldInfo; import io.github.classgraph.ScanResult; /** @@ -31,9 +30,7 @@ public void recordJar() throws Exception { assertThat(scanResult.getAllEnums().size() == 1); final ClassInfo myEnum = scanResult.getAllEnums().get(0); assertThat(myEnum.getName().equals(MyEnum.class.getName())); - for (FieldInfo fi : myEnum.getFieldInfo()) { - System.out.println(fi); - } + assertThat(myEnum.getEnumConstants().getNames()).containsExactly("A", "B", "C"); } } } From 1fbc68f59b6b196d34026f826fd7ec1fac13fa65 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 Dec 2021 10:15:48 -0700 Subject: [PATCH 297/713] Fix comments and names --- .../java/io/github/classgraph/features/EnumTest.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/EnumTest.java b/src/test/java/io/github/classgraph/features/EnumTest.java index 4d83c6b8c..8580db8f6 100644 --- a/src/test/java/io/github/classgraph/features/EnumTest.java +++ b/src/test/java/io/github/classgraph/features/EnumTest.java @@ -17,14 +17,9 @@ private static enum MyEnum { A, B, C; } - /** - * Test records (JDK 14+). - * - * @throws Exception - * the exception - */ + /** Test */ @Test - public void recordJar() throws Exception { + public void enumConsts() throws Exception { try (ScanResult scanResult = new ClassGraph().acceptClasses(MyEnum.class.getName()).enableAllInfo() .scan()) { assertThat(scanResult.getAllEnums().size() == 1); From 1028925b67bb23ad8f3d8e168124e02ac53dfa9a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 Dec 2021 02:09:22 -0700 Subject: [PATCH 298/713] Source > Format --- src/main/java/nonapi/io/github/classgraph/utils/LogNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java index 7b686e096..9004567a1 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java @@ -54,7 +54,7 @@ public final class LogNode { static { System.getProperties().setProperty("log4j2.formatMsgNoLookups", "true"); } - + /** The logger. */ private static final Logger log = Logger.getLogger(ClassGraph.class.getName()); From 04223e82c8fe5bd29f561dad8c4d1003c8d1fbad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 Dec 2021 02:12:06 -0700 Subject: [PATCH 299/713] Add `getEnumConstantObjects()` (#608) --- .../java/io/github/classgraph/ClassInfo.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 7da5602cc..5c2a6dea5 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -53,6 +53,7 @@ import io.github.classgraph.Classfile.ClassTypeAnnotationDecorator; import io.github.classgraph.FieldInfoList.FieldInfoFilter; import nonapi.io.github.classgraph.json.Id; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; @@ -2667,19 +2668,40 @@ public FieldInfoList getFieldInfo() { return fieldInfoList; } - /** Get all enum constants of an enum class (as a list of {@link FieldInfo} objects). */ + /** + * @return All enum constants of an enum class as a list of {@link FieldInfo} objects (enum constants are stored + * as fields in Java classes). + */ public FieldInfoList getEnumConstants() { if (!isEnum()) { throw new IllegalArgumentException("Class " + getName() + " is not an enum"); } return getFieldInfo().filter(new FieldInfoFilter() { @Override - public boolean accept(FieldInfo fieldInfo) { + public boolean accept(final FieldInfo fieldInfo) { return fieldInfo.isEnum(); } }); } - + + /** @return All enum constants of an enum class as a list of objects of the same type as the enum. */ + public List getEnumConstantObjects() { + if (!isEnum()) { + throw new IllegalArgumentException("Class " + getName() + " is not an enum"); + } + final Class enumClass = loadClass(); + final FieldInfoList consts = getEnumConstants(); + final List constObjs = new ArrayList<>(consts.size()); + for (final FieldInfo constFieldInfo : consts) { + final Object constObj = ReflectionUtils.getStaticFieldVal(true, enumClass, constFieldInfo.getName()); + if (constObj == null) { + throw new IllegalArgumentException("Could not read enum constant objects"); + } + constObjs.add(constObj); + } + return constObjs; + } + /** * Returns information on the named field declared by the class, but not by its superclasses. See also: * From 1c8adca1f65e6eda7303a592be9dbdc40e72e556 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 Dec 2021 02:12:12 -0700 Subject: [PATCH 300/713] Improve test --- .../github/classgraph/features/EnumTest.java | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/EnumTest.java b/src/test/java/io/github/classgraph/features/EnumTest.java index 8580db8f6..edab8718a 100644 --- a/src/test/java/io/github/classgraph/features/EnumTest.java +++ b/src/test/java/io/github/classgraph/features/EnumTest.java @@ -12,20 +12,48 @@ * Test. */ public class EnumTest { - /** Enum */ - private static enum MyEnum { + /** Regular enum */ + private static enum MyEnumWithoutMethod { A, B, C; } - /** Test */ + private static enum EnumWithMethod { + P(1), Q(2); + + int val; + + EnumWithMethod(final int val) { + this.val = val; + } + + int getVal() { + return val; + } + }; + + /** Test regular enum */ @Test - public void enumConsts() throws Exception { - try (ScanResult scanResult = new ClassGraph().acceptClasses(MyEnum.class.getName()).enableAllInfo() - .scan()) { + public void enumWithoutMethod() throws Exception { + try (ScanResult scanResult = new ClassGraph().acceptClasses(MyEnumWithoutMethod.class.getName()) + .enableAllInfo().scan()) { assertThat(scanResult.getAllEnums().size() == 1); final ClassInfo myEnum = scanResult.getAllEnums().get(0); - assertThat(myEnum.getName().equals(MyEnum.class.getName())); + assertThat(myEnum.getName().equals(MyEnumWithoutMethod.class.getName())); assertThat(myEnum.getEnumConstants().getNames()).containsExactly("A", "B", "C"); } } + + /** Test enum with method */ + @Test + public void enumWithMethod() throws Exception { + try (ScanResult scanResult = new ClassGraph().acceptClasses(EnumWithMethod.class.getName()).enableAllInfo() + .scan()) { + assertThat(scanResult.getAllEnums().size() == 1); + final ClassInfo myEnum = scanResult.getAllEnums().get(0); + assertThat(myEnum.getName().equals(EnumWithMethod.class.getName())); + assertThat(myEnum.getEnumConstants().getNames()).containsExactly("P", "Q"); + assertThat(((EnumWithMethod) myEnum.getEnumConstantObjects().get(0)).getVal()).isEqualTo(1); + assertThat(((EnumWithMethod) myEnum.getEnumConstantObjects().get(1)).getVal()).isEqualTo(2); + } + } } From ace4dfe6c99a5617b09eb803575d7316307455e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 Dec 2021 02:15:20 -0700 Subject: [PATCH 301/713] [maven-release-plugin] prepare release classgraph-4.8.138 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e0d9bc2c1..f8c729372 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.138-SNAPSHOT + 4.8.138 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.138 From 3a7ff9484314e57b2c06225ede06c70224381ca5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 Dec 2021 02:15:22 -0700 Subject: [PATCH 302/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f8c729372..cd3f85f43 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.138 + 4.8.139-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.138 + HEAD From c62b333b9ed006f45c4dc8c5bec41948afe07ce0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 02:01:16 +0000 Subject: [PATCH 303/713] Bump jvm-driver from 8.7.5 to 8.7.10 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.7.5 to 8.7.10. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.7.5...jvm-driver-8.7.10) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..36c627993 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.5 + 8.7.10 true From f97603a32eef30eca296d4549138128da8814f04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Dec 2021 02:01:37 +0000 Subject: [PATCH 304/713] Bump jmh-core from 1.33 to 1.34 Bumps jmh-core from 1.33 to 1.34. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..37ca5fd81 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ org.openjdk.jmh jmh-core - 1.33 + 1.34 test From fff4faf0360d974c493be1bdd1f2ca79521dafab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Dec 2021 02:01:39 +0000 Subject: [PATCH 305/713] Bump jmh-generator-annprocess from 1.33 to 1.34 Bumps jmh-generator-annprocess from 1.33 to 1.34. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..ff7d3d74a 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.33 + 1.34 test From 05260b8b0f891964fd08c14ea710d79788549626 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Dec 2021 02:01:32 +0000 Subject: [PATCH 306/713] Bump maven-site-plugin from 3.9.1 to 3.10.0 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.9.1 to 3.10.0. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.9.1...maven-site-plugin-3.10.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-site-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..70e48a662 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ org.apache.maven.plugins maven-site-plugin - 3.9.1 + 3.10.0 From c2e2fa37a1398eca4b79092a79fba742cff410a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Dec 2021 02:01:07 +0000 Subject: [PATCH 307/713] Bump maven-deploy-plugin from 3.0.0-M1 to 3.0.0-M2 Bumps [maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.0.0-M1 to 3.0.0-M2. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.0.0-M1...maven-deploy-plugin-3.0.0-M2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..ce3fd8b6c 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M1 + 3.0.0-M2 org.apache.maven.plugins From 65bbb633db990cddae6bbc24801a490b8811346e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Jan 2022 15:26:45 -0700 Subject: [PATCH 308/713] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1567d7f9..a30ec2164 100644 --- a/README.md +++ b/README.md @@ -113,13 +113,15 @@ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That wi * The legacy classloader does not expose its classpath via a public field or method * The classloader is loaded in a different module from your user code -**If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem.** +If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem. ClassGraph can use either of the following libraries to silently circumvent all of Java's security mechanisms (visibility/access checks, security manager restrictions, and strong encapsulation), in order to read the classpath from private fields and methods of classloaders. * Narcissus by Luke Hutchison (@lukehutch), author of ClassGraph * JVM-Driver is by Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core). +**To clarify, you do *only* need to use Narcissus or JVM-driver if ClassGraph cannot find the classpath elements from your classloader, due to the enforcement of strong encapsulation, or if it is problematic that you are getting reflection access warnings on the console.** + To use one of these libraries: * Upgrade ClassGraph to at least version 4.8.124 From 45d5b991aed611abd228efcd2ecd40ba02489f24 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Jan 2022 23:09:07 -0700 Subject: [PATCH 309/713] Fix NPE in close() --- .../github/classgraph/utils/ProxyingInputStream.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java index 2d9c116d9..0e24abe67 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java @@ -194,10 +194,12 @@ public String toString() { @Override public void close() throws IOException { - try { - inputStream.close(); - } finally { - inputStream = null; + if (inputStream != null) { + try { + inputStream.close(); + } finally { + inputStream = null; + } } } } From 9d0664619d1a49647247d8bb9e5f39240610a691 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:15:20 -0700 Subject: [PATCH 310/713] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a30ec2164..4a7cf7c10 100644 --- a/README.md +++ b/README.md @@ -108,10 +108,9 @@ See instructions for [use as a module](https://github.com/classgraph/classgraph/ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That will means that by default, ClassGraph will not be able to find the classpath of your project, if all of the following are true: -* You are running on JDK 16+ -* You are using a legacy classloader (rather than the module system) -* The legacy classloader does not expose its classpath via a public field or method -* The classloader is loaded in a different module from your user code +* You are running on JDK 16+, and either: + * You are using a legacy classloader (rather than the module system), and the classloader does not expose its classpath via a public field or method; or: + * The classloader is loaded in a different module from your user code If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem. From f02d240487e3c34f57303a8f0fcc21f610ec54d5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:16:08 -0700 Subject: [PATCH 311/713] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a7cf7c10..71fa39320 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,9 @@ See instructions for [use as a module](https://github.com/classgraph/classgraph/ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That will means that by default, ClassGraph will not be able to find the classpath of your project, if all of the following are true: -* You are running on JDK 16+, and either: - * You are using a legacy classloader (rather than the module system), and the classloader does not expose its classpath via a public field or method; or: +* You are running on JDK 16+ +* the classloader does not expose its classpath via a public field or method, and either: + * You are using a legacy classloader (rather than the module system), or: * The classloader is loaded in a different module from your user code If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem. From ba9eb7694dd821438dfaab57e880dee0d461fde8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:18:52 -0700 Subject: [PATCH 312/713] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 71fa39320..ad34daaa1 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,9 @@ See instructions for [use as a module](https://github.com/classgraph/classgraph/ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That will means that by default, ClassGraph will not be able to find the classpath of your project, if all of the following are true: * You are running on JDK 16+ -* the classloader does not expose its classpath via a public field or method, and either: +* Your classloader does not expose its classpath via a public field or method, and either: * You are using a legacy classloader (rather than the module system), or: - * The classloader is loaded in a different module from your user code + * The classloader is loaded in a different module from your user code. If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem. From c5e1f097c6a018a74e01248c1e027f47fad6d09d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:38:19 -0700 Subject: [PATCH 313/713] Fix for deprecation warning in JDK 18 --- .../io/github/classgraph/fileslice/FileSlice.java | 15 ++++++++++++++- .../classgraph/reflection/ReflectionUtils.java | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java index 2635a8d2c..1821ddc86 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -44,6 +45,7 @@ import nonapi.io.github.classgraph.fileslice.reader.RandomAccessByteBufferReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileChannelReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -70,6 +72,10 @@ public class FileSlice extends Slice { /** True if {@link #close} has been called. */ private final AtomicBoolean isClosed = new AtomicBoolean(); + /** System.runFinalization() -- deprecated in JDK 18, so accessed by reflection. */ + private static final Method runFinalizationMethod = ReflectionUtils.staticMethodForNameOrNull("System", + "runFinalization"); + /** * Constructor for treating a range of a file as a slice. * @@ -144,7 +150,14 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i } catch (IOException | OutOfMemoryError e) { // Try running garbage collection then try mapping the file again System.gc(); - System.runFinalization(); + if (runFinalizationMethod != null) { + try { + // Call System.runFinalization() (deprecated in JDK 18) + runFinalizationMethod.invoke(null); + } catch (final Throwable t) { + // Ignore + } + } try { backingByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0L, fileLength); } catch (IOException | OutOfMemoryError e2) { diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 02791b5c5..cad2cef78 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -369,6 +369,21 @@ public static Class classForNameOrNull(final String className) { } } + /** + * Get a static method by name, but return null if any exception is thrown. + * + * @param className + * The class name to load. + * @return The class of the requested name, or null if an exception was thrown while trying to load the class. + */ + public static Method staticMethodForNameOrNull(final String className, final String staticMethodName) { + try { + return reflectionDriver.findStaticMethod(reflectionDriver.findClass(className), staticMethodName); + } catch (final Throwable e) { + return null; + } + } + // ------------------------------------------------------------------------------------------------------------- private static class PrivilegedActionInvocationHandler implements InvocationHandler { From 243b9b430b23957636b7beaf835cca87ad6bac0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:39:09 +0000 Subject: [PATCH 314/713] Bump maven-release-plugin from 3.0.0-M4 to 3.0.0-M5 Bumps [maven-release-plugin](https://github.com/apache/maven-release) from 3.0.0-M4 to 3.0.0-M5. - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0-M4...maven-release-3.0.0-M5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..216f72ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.5 + 8.7.10 true @@ -219,7 +219,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M4 + 3.0.0-M5 @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M1 + 3.0.0-M2 org.apache.maven.plugins From 0ef98a1a14bc59ee246789cf8bb30adcc7f6be64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:39:12 +0000 Subject: [PATCH 315/713] Bump build-helper-maven-plugin from 3.2.0 to 3.3.0 Bumps [build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/build-helper-maven-plugin-3.2.0...build-helper-maven-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..c6240d1cb 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.5 + 8.7.10 true @@ -184,7 +184,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.2.0 + 3.3.0 org.apache.maven.plugins @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M1 + 3.0.0-M2 org.apache.maven.plugins From c2663d54ea3c7fcb0a2e7f28b26c0afa0df49962 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:39:15 +0000 Subject: [PATCH 316/713] Bump maven-jar-plugin from 3.2.0 to 3.2.1 Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.0...maven-jar-plugin-3.2.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..460f6fb29 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.5 + 8.7.10 true @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.2.1 org.apache.maven.plugins @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M1 + 3.0.0-M2 org.apache.maven.plugins From 1267490b6b933edd9900bd4e308a24f80fb58c82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:39:19 +0000 Subject: [PATCH 317/713] Bump assertj-core from 3.21.0 to 3.22.0 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.22.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.22.0) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cd3f85f43..5b3fd3cb6 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.5 + 8.7.10 true @@ -101,7 +101,7 @@ org.assertj assertj-core - 3.21.0 + 3.22.0 test @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M1 + 3.0.0-M2 org.apache.maven.plugins From dd47c24621e624752c24b1ae14ee7efa39298f7a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:45:55 -0700 Subject: [PATCH 318/713] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ad34daaa1..c233f3dca 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,8 @@ See instructions for [use as a module](https://github.com/classgraph/classgraph/ The JDK team decided to start enforcing strong encapsulation in JDK 16+. That will means that by default, ClassGraph will not be able to find the classpath of your project, if all of the following are true: * You are running on JDK 16+ -* Your classloader does not expose its classpath via a public field or method, and either: - * You are using a legacy classloader (rather than the module system), or: - * The classloader is loaded in a different module from your user code. +* You are using a legacy classloader (rather than the module system) +* Your classloader does not expose its classpath via a public field or method (i.e. the full classpath can only be determined by reflection of private fields or methods). If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ (meaning that ClassGraph can no longer find your classes), you have probably run into this problem. From 6704f6adbe3cadb90f1e0dff3b846d2935893cdf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:47:02 -0700 Subject: [PATCH 319/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c233f3dca..f3ef13118 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ If your ClassGraph code works in JDK versions less than 16 but breaks in JDK 16+ ClassGraph can use either of the following libraries to silently circumvent all of Java's security mechanisms (visibility/access checks, security manager restrictions, and strong encapsulation), in order to read the classpath from private fields and methods of classloaders. * Narcissus by Luke Hutchison (@lukehutch), author of ClassGraph -* JVM-Driver is by Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core). +* JVM-Driver by Roberto Gentili (@burningwave), author of [Burningwave Core](https://github.com/burningwave/core). **To clarify, you do *only* need to use Narcissus or JVM-driver if ClassGraph cannot find the classpath elements from your classloader, due to the enforcement of strong encapsulation, or if it is problematic that you are getting reflection access warnings on the console.** From 01171945a32264b17a56e809827298cc1c8e7569 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:47:55 -0700 Subject: [PATCH 320/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3ef13118..fc12cc51c 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ ClassGraph can use either of the following libraries to silently circumvent all To use one of these libraries: -* Upgrade ClassGraph to at least version 4.8.124 +* Upgrade ClassGraph to the latest version * Either: 1. Add the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (this includes a native library, and only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). From 533607777d1df62d76ac06369f19cd659982d5a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:48:40 -0700 Subject: [PATCH 321/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc12cc51c..b06f01d08 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ Some other classpath scanning mechanisms include: **The MIT License (MIT)** -**Copyright (c) 2020 Luke Hutchison** +**Copyright (c) 2022 Luke Hutchison** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: From 9df7ce0484effaa2a5f3d9c698a75160a92efecb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:50:16 -0700 Subject: [PATCH 322/713] Improve error logging (#625) --- src/main/java/io/github/classgraph/Scanner.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 371941359..fdb9c9c5c 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -587,13 +587,9 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, try { classpathElt = classpathEntryToClasspathElementSingletonMap.get(workUnit.rawClasspathEntry, log); - } catch (final NullSingletonException e) { + } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for classpath entry " + workUnit.rawClasspathEntry + " : " + e); - } catch (final NewInstanceException e) { - throw new IOException( - "Cannot get classpath element for classpath entry " + workUnit.rawClasspathEntry, - e); } // Only run open() once per ClasspathElement (it is possible for there to be From 4e02cc871ed9480be4fce5b80a448cde2f6b1958 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 11:53:49 -0700 Subject: [PATCH 323/713] Improve logging when `NewInstanceException` is thrown --- .../java/io/github/classgraph/ClasspathElementModule.java | 8 +------- .../java/io/github/classgraph/ClasspathElementZip.java | 8 +++----- src/main/java/io/github/classgraph/Scanner.java | 6 +----- .../classgraph/fastzipfilereader/NestedJarHandler.java | 5 +---- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 28c77aea6..7ea8abf9f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -113,18 +113,12 @@ void open(final WorkQueue workQueueIgnored, final LogNod } try { moduleReaderProxyRecycler = moduleRefToModuleReaderProxyRecyclerMap.get(moduleRef, log); - } catch (final IOException | NullSingletonException e) { + } catch (final IOException | NullSingletonException | NewInstanceException e) { if (log != null) { log(classpathElementIdx, "Skipping invalid module " + getModuleName() + " : " + e, log); } skipClasspathElement = true; return; - } catch (final NewInstanceException e) { - if (log != null) { - log(classpathElementIdx, "Skipping invalid module " + getModuleName(), e, log); - } - skipClasspathElement = true; - return; } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 1764c603b..f1a632122 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -161,12 +161,10 @@ void open(final WorkQueue workQueue, final LogNode log) try { logicalZipFileAndPackageRoot = nestedJarHandler.nestedPathToLogicalZipFileAndPackageRootMap .get(rawPath, subLog); - } catch (final NullSingletonException e) { - // Generally thrown on the second and subsequent attempt to call .get(), after the first failed + } catch (final NullSingletonException | NewInstanceException e) { + // Generally thrown on the second and subsequent attempt to call .get(), after the first failed, + // or newInstance() threw an exception throw new IOException("Could not get logical zipfile " + rawPath + " : " + e); - } catch (final NewInstanceException e) { - // newInstance() threw an exception - throw new IOException("Could not get logical zipfile " + rawPath, e); } logicalZipFile = logicalZipFileAndPackageRoot.getKey(); if (logicalZipFile == null) { diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index fdb9c9c5c..44ef5aa99 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -541,13 +541,9 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa try { return this.get(new ClasspathElementAndClassLoader(canonicalPathNormalized, dirOrPathPackageRoot, classpathEntry.classLoader), log); - } catch (final NullSingletonException e) { + } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for canonical path " + canonicalPathNormalized + " : " + e); - } catch (final NewInstanceException e) { - throw new IOException( - "Cannot get classpath element for canonical path " + canonicalPathNormalized, - e); } } else { // Otherwise path is already canonical, and this is the first time this path has diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index c845fff0c..f3043e243 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -188,13 +188,10 @@ public Entry newInstance(final String nestedJarPathRaw, final File canonicalFile = new File(nestedJarPath).getCanonicalFile(); // Get or create a PhysicalZipFile instance for the canonical file physicalZipFile = canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); - } catch (final NullSingletonException e) { + } catch (final NullSingletonException | NewInstanceException e) { // If getting PhysicalZipFile failed, re-wrap in IOException throw new IOException( "Could not get PhysicalZipFile for path " + nestedJarPath + " : " + e); - } catch (final NewInstanceException e) { - // If getting PhysicalZipFile failed, re-wrap in IOException - throw new IOException("Could not get PhysicalZipFile for path " + nestedJarPath, e); } catch (final SecurityException e) { // getCanonicalFile() failed (it may have also failed with IOException) throw new IOException( From 3f24c7b28944c948d2aff696ffe420fbe8bbbff3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 19:19:12 -0700 Subject: [PATCH 324/713] Improve logging --- .../nonapi/io/github/classgraph/concurrency/SingletonMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java index 988ca11f0..90a869ed1 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -90,7 +90,7 @@ public static class NewInstanceException extends Exception { * the Throwable that was thrown */ public NewInstanceException(final K key, final Throwable t) { - super("newInstance threw an exception for key " + key, t); + super("newInstance threw an exception for key " + key + " : " + t, t); } } From beccb5b7b056ca29a43b7bfddb5fcd01e9a75286 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Jan 2022 19:29:20 -0700 Subject: [PATCH 325/713] Strip prefixes in any order (#625) --- .../classgraph/utils/FastPathResolver.java | 109 ++++++++++-------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index abfe6f824..27f17adfc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -200,60 +200,71 @@ public static String resolve(final String resolveBasePath, final String relative boolean isAbsolutePath = false; boolean isFileOrJarURL = false; int startIdx = 0; - if (relativePath.regionMatches(true, 0, "jar:", 0, 4)) { - // "jar:" prefix can be stripped - startIdx = 4; - isFileOrJarURL = true; - } - if (relativePath.regionMatches(true, startIdx, "http://", 0, 7)) { - // Detect http:// - startIdx += 7; - // Force protocol name to lowercase - prefix = "http://"; - // Treat the part after the protocol as an absolute path, so the domain is not treated as a directory - // relative to the current directory. - isAbsolutePath = true; - // Don't un-escape percent encoding etc. - } else if (relativePath.regionMatches(true, startIdx, "https://", 0, 8)) { - // Detect https:// - startIdx += 8; - prefix = "https://"; - isAbsolutePath = true; - } else if (relativePath.regionMatches(true, startIdx, "jrt:", 0, 5)) { - // Detect jrt: - startIdx += 4; - prefix = "jrt:"; - isAbsolutePath = true; - } else if (relativePath.regionMatches(true, startIdx, "file:", 0, 5)) { - // Strip off "file:" prefix from relative path - startIdx += 5; - while (startIdx < relativePath.length() - 1 && relativePath.charAt(startIdx) == '/' - && relativePath.charAt(startIdx + 1) == '/') { - // Strip off all but one '/' after "file:" - startIdx++; - } - isFileOrJarURL = true; - } else { - // Preserve the number of slashes on custom URL schemes (#420) - final String relPath = startIdx == 0 ? relativePath : relativePath.substring(startIdx); - final Matcher m2 = schemeTwoSlashMatcher.matcher(relPath); - if (m2.find()) { - final String m2Match = m2.group(); - startIdx += m2Match.length(); - prefix = m2Match; - // Treat the part after the protocol as an absolute path, so the rest of the URL is not treated - // as a directory relative to the current directory. + boolean matchedPrefix; + do { + matchedPrefix = false; + if (relativePath.regionMatches(true, startIdx, "jar:", 0, 4)) { + // "jar:" prefix can be stripped + matchedPrefix = true; + startIdx = 4; + isFileOrJarURL = true; + } else if (relativePath.regionMatches(true, startIdx, "http://", 0, 7)) { + // Detect http:// + matchedPrefix = true; + startIdx += 7; + // Force protocol name to lowercase + prefix += "http://"; + // Treat the part after the protocol as an absolute path, so the domain is not treated as a directory + // relative to the current directory. isAbsolutePath = true; + // Don't un-escape percent encoding etc. + } else if (relativePath.regionMatches(true, startIdx, "https://", 0, 8)) { + // Detect https:// + matchedPrefix = true; + startIdx += 8; + prefix += "https://"; + isAbsolutePath = true; + } else if (relativePath.regionMatches(true, startIdx, "jrt:", 0, 5)) { + // Detect jrt: + matchedPrefix = true; + startIdx += 4; + prefix += "jrt:"; + isAbsolutePath = true; + } else if (relativePath.regionMatches(true, startIdx, "file:", 0, 5)) { + // Strip off "file:" prefix from relative path + matchedPrefix = true; + startIdx += 5; + while (startIdx < relativePath.length() - 1 && relativePath.charAt(startIdx) == '/' + && relativePath.charAt(startIdx + 1) == '/') { + // Strip off all but one '/' after "file:" + startIdx++; + } + isFileOrJarURL = true; } else { - final Matcher m1 = schemeOneSlashMatcher.matcher(relPath); - if (m1.find()) { - final String m1Match = m1.group(); - startIdx += m1Match.length(); - prefix = m1Match; + // Preserve the number of slashes on custom URL schemes (#420) + final String relPath = startIdx == 0 ? relativePath : relativePath.substring(startIdx); + final Matcher m2 = schemeTwoSlashMatcher.matcher(relPath); + if (m2.find()) { + matchedPrefix = true; + final String m2Match = m2.group(); + startIdx += m2Match.length(); + prefix += m2Match; + // Treat the part after the protocol as an absolute path, so the rest of the URL is not treated + // as a directory relative to the current directory. isAbsolutePath = true; + } else { + final Matcher m1 = schemeOneSlashMatcher.matcher(relPath); + if (m1.find()) { + matchedPrefix = true; + final String m1Match = m1.group(); + startIdx += m1Match.length(); + prefix += m1Match; + isAbsolutePath = true; + } } } - } + } while (matchedPrefix); + if (isFileOrJarURL) { if (WINDOWS) { if (relativePath.startsWith("\\\\\\\\", startIdx) || relativePath.startsWith("////", startIdx)) { From 9984d099dd01617e4c84cdc94d7f5b95b97901f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 02:02:06 +0000 Subject: [PATCH 326/713] Bump maven-compiler-plugin from 3.8.1 to 3.9.0 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.8.1 to 3.9.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.9.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7fbe2699b..8f37181d7 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.9.0 org.apache.maven.plugins From f92826ccded28b7b035e4eacd87500451955b368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jan 2022 02:02:10 +0000 Subject: [PATCH 327/713] Bump maven-jar-plugin from 3.2.1 to 3.2.2 Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.1...maven-jar-plugin-3.2.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7fbe2699b..ce0a9da93 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.1 + 3.2.2 org.apache.maven.plugins From fbd565879177df9c86f34aed5134d138daed3621 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 02:02:11 +0000 Subject: [PATCH 328/713] Bump slf4j-api from 2.0.0-alpha5 to 2.0.0-alpha6 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha5 to 2.0.0-alpha6. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha5...v_2.0.0-alpha6) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7fbe2699b..7aecd2842 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.0-alpha5 + 2.0.0-alpha6 test From e0c5b3318a472108d7bec1bc0a26df581e1584ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 02:02:14 +0000 Subject: [PATCH 329/713] Bump slf4j-jdk14 from 2.0.0-alpha5 to 2.0.0-alpha6 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha5 to 2.0.0-alpha6. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha5...v_2.0.0-alpha6) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7fbe2699b..67a5a2446 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha5 + 2.0.0-alpha6 test From 441eb0249123ee5322ac10492f82435bf81b6fbc Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Fri, 14 Jan 2022 11:41:43 -0800 Subject: [PATCH 330/713] Add thrown exceptions to MethodInfo (#633) --- .../java/io/github/classgraph/Classfile.java | 10 +++- .../java/io/github/classgraph/MethodInfo.java | 53 ++++++++++++++++++- .../issues/issue175/Issue175Test.java | 4 +- .../test/methodinfo/MethodInfoTest.java | 31 ++++++++++- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 0024c41bb..b373799fe 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1448,6 +1448,7 @@ private void readMethods() throws IOException, ClassfileFormatException { } final int attributesCount = reader.readUnsignedShort(); String[] methodParameterNames = null; + String[] thrownExceptionNames = null; int[] methodParameterModifiers = null; AnnotationInfo[][] methodParameterAnnotations = null; AnnotationInfoList methodAnnotationInfo = null; @@ -1662,6 +1663,13 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { this.annotationParamDefaultValues.add(new AnnotationParameterValue(methodName, // Get annotation parameter default value readAnnotationElementValue())); + } else if (constantPoolStringEquals(attributeNameCpIdx, "Exceptions")) { + final int exceptionCount = reader.readUnsignedShort(); + thrownExceptionNames = new String[exceptionCount]; + for (int k = 0; k < exceptionCount; k++) { + final int cpIdx = reader.readUnsignedShort(); + thrownExceptionNames[k] = getConstantPoolClassName(cpIdx); + } } else if (constantPoolStringEquals(attributeNameCpIdx, "Code")) { methodHasBody = true; reader.skip(attributeLength); @@ -1677,7 +1685,7 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { methodInfoList.add(new MethodInfo(className, methodName, methodAnnotationInfo, methodModifierFlags, methodTypeDescriptor, methodTypeSignatureStr, methodParameterNames, methodParameterModifiers, methodParameterAnnotations, methodHasBody, - methodTypeAnnotationDecorators)); + methodTypeAnnotationDecorators, thrownExceptionNames)); } } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 410f0f06c..8eaaee943 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -106,6 +106,10 @@ public class MethodInfo extends ScanResultObject implements Comparable typeAnnotationDecorators; + private String[] thrownExceptionNames; + + private transient ClassInfoList thrownExceptions; + // ------------------------------------------------------------------------------------------------------------- /** Default constructor for deserialization. */ @@ -141,7 +145,8 @@ public class MethodInfo extends ScanResultObject implements Comparable methodTypeAnnotationDecorators) { + final List methodTypeAnnotationDecorators, + final String[] thrownExceptionNames) { super(); this.declaringClassName = definingClassName; this.name = methodName; @@ -155,6 +160,7 @@ public class MethodInfo extends ScanResultObject implements Comparable classNameToC } } } + if (thrownExceptionNames != null) { + ClassInfoList thrownExceptions = getThrownExceptions(); + if (thrownExceptions != null) { + for (int i = 0; i < thrownExceptions.size(); i++) { + if(thrownExceptions.get(i) != null) { + classNameToClassInfo.put(thrownExceptionNames[i], thrownExceptions.get(i)); + } + } + } + } } // ------------------------------------------------------------------------------------------------------------- @@ -1115,6 +1154,7 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { } buf.append(')'); + // when throws signature is present, it includes both generic type variables and class names if (!methodType.getThrowsSignatures().isEmpty()) { buf.append(" throws "); for (int i = 0; i < methodType.getThrowsSignatures().size(); i++) { @@ -1123,6 +1163,17 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { } methodType.getThrowsSignatures().get(i).toString(useSimpleNames, buf); } + } else { + if (thrownExceptionNames != null && thrownExceptionNames.length > 0) { + buf.append(" throws "); + for (int i = 0; i < thrownExceptionNames.length; i++) { + if (i > 0) { + buf.append(", "); + } + buf.append(useSimpleNames ? ClassInfo.getSimpleName(thrownExceptionNames[i]) : + thrownExceptionNames[i].replace('$', '.')); + } + } } } } diff --git a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java index cfb017f06..b2d0ebc3c 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -272,8 +272,8 @@ public void testResultTypeReconciliationIssue() { "@org.jetbrains.annotations.NotNull public final java.util.Map> getMethodParamNames()", "@org.jetbrains.annotations.NotNull public java.util.List paramNamesFromMethod(@org.jetbrains.annotations.NotNull java.lang.reflect.Method method)", "@org.jetbrains.annotations.NotNull public java.util.List paramNamesFromConstructor(@org.jetbrains.annotations.NotNull java.lang.reflect.Constructor ctor)", - "@org.jetbrains.annotations.NotNull public final net.corda.client.jackson.StringToMethodCallParser.ParsedMethodCall parse(@org.jetbrains.annotations.Nullable T target, @org.jetbrains.annotations.NotNull java.lang.String command)", - "@org.jetbrains.annotations.NotNull public final java.lang.Object[] parseArguments(@org.jetbrains.annotations.NotNull java.lang.String methodNameHint, @org.jetbrains.annotations.NotNull java.util.List>> parameters, @org.jetbrains.annotations.NotNull java.lang.String args)", + "@org.jetbrains.annotations.NotNull public final net.corda.client.jackson.StringToMethodCallParser.ParsedMethodCall parse(@org.jetbrains.annotations.Nullable T target, @org.jetbrains.annotations.NotNull java.lang.String command) throws net.corda.client.jackson.StringToMethodCallParser.UnparseableCallException", + "@org.jetbrains.annotations.NotNull public final java.lang.Object[] parseArguments(@org.jetbrains.annotations.NotNull java.lang.String methodNameHint, @org.jetbrains.annotations.NotNull java.util.List>> parameters, @org.jetbrains.annotations.NotNull java.lang.String args) throws net.corda.client.jackson.StringToMethodCallParser.UnparseableCallException", "@org.jetbrains.annotations.NotNull public final java.util.Map getAvailableCommands()", "@kotlin.jvm.JvmOverloads public (@org.jetbrains.annotations.NotNull java.lang.Class targetType, @org.jetbrains.annotations.NotNull com.fasterxml.jackson.databind.ObjectMapper om)", "@kotlin.jvm.JvmOverloads public synthetic (java.lang.Class, com.fasterxml.jackson.databind.ObjectMapper, int, kotlin.jvm.internal.DefaultConstructorMarker)", diff --git a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java index 5c1316606..82cb4340f 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -54,7 +54,7 @@ public class MethodInfoTest { /** * The Class X. */ - public static class X { + public static class X extends Exception { /** * Method. */ @@ -99,6 +99,12 @@ private static String[] privateMethod() { return null; } + public void throwsException() throws X { + } + + public void throwsGenericException() throws X, X2 { + } + /** * Method info not enabled. */ @@ -134,10 +140,13 @@ public boolean accept(final MethodInfo methodInfo) { + "final java.util.List l, " + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " + "final java.lang.String[]... varargs)", + "public void throwsException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X", + "public void throwsGenericException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X, X2", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", "@" + Test.class.getName() + " public void testGetConstructorInfo()", "@" + Test.class.getName() + " public void testGetMethodInfoIgnoringVisibility()", + "@" + Test.class.getName() + " public void testGetThrownExceptions()", "@" + Test.class.getName() + " public void testMethodInfoLoadMethodForArrayArg()"); } } @@ -177,10 +186,13 @@ public boolean accept(final MethodInfo methodInfo) { + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " + "final java.lang.String[]... varargs)", "private static java.lang.String[] privateMethod()", + "public void throwsException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X", + "public void throwsGenericException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X, X2", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", "@" + Test.class.getName() + " public void testGetConstructorInfo()", "@" + Test.class.getName() + " public void testGetMethodInfoIgnoringVisibility()", + "@" + Test.class.getName() + " public void testGetThrownExceptions()", "@" + Test.class.getName() + " public void testMethodInfoLoadMethodForArrayArg()"); } } @@ -228,4 +240,21 @@ public void testMethodInfoLoadMethodForArrayArg() { assertThat(p3.getNumDimensions()).isEqualTo(2); } } + + @Test + public void testGetThrownExceptions() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) + .enableClassInfo().enableMethodInfo().scan()) { + MethodInfo mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() + .getSingleMethod("throwsException"); + assertThat(mi.getThrownExceptions()).hasSize(1); + assertThat(mi.getThrownExceptions().get(0).getSimpleName()).isEqualTo("X"); + + mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() + .getSingleMethod("throwsGenericException"); + assertThat(mi.getThrownExceptions()).hasSize(2); + assertThat(mi.getThrownExceptions().get(0).getSimpleName()).isEqualTo("X"); + assertThat(mi.getThrownExceptions().get(1).getSimpleName()).isEqualTo("X"); + } + } } From b9b60bd801d11d56b68f4dbe0dd8fd2738b6ab94 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Fri, 14 Jan 2022 15:22:50 -0800 Subject: [PATCH 331/713] Filter out thrown exceptions with null ClassInfo --- src/main/java/io/github/classgraph/MethodInfo.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 8eaaee943..e783f59f8 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -329,8 +329,8 @@ public ClassInfoList getThrownExceptions() { thrownExceptions = new ClassInfoList(thrownExceptionNames.length); for (String thrownExceptionName : thrownExceptionNames) { ClassInfo classInfo = scanResult.getClassInfo(thrownExceptionName); - thrownExceptions.add(classInfo); if (classInfo != null) { + thrownExceptions.add(classInfo); classInfo.setScanResult(scanResult); } } @@ -952,9 +952,7 @@ protected void findReferencedClassInfo(final Map classNameToC ClassInfoList thrownExceptions = getThrownExceptions(); if (thrownExceptions != null) { for (int i = 0; i < thrownExceptions.size(); i++) { - if(thrownExceptions.get(i) != null) { - classNameToClassInfo.put(thrownExceptionNames[i], thrownExceptions.get(i)); - } + classNameToClassInfo.put(thrownExceptionNames[i], thrownExceptions.get(i)); } } } From f731358c6147cc00e9b7269216c2c10a1f5169ae Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Fri, 14 Jan 2022 15:25:18 -0800 Subject: [PATCH 332/713] extendScanningUpwards from method thrown exceptions --- src/main/java/io/github/classgraph/Classfile.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index b373799fe..d3a39c62a 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -426,6 +426,11 @@ private void extendScanningUpwards(final LogNode log) { } } } + if (methodInfo.getThrownExceptionNames() != null) { + for (final String thrownExceptionName : methodInfo.getThrownExceptionNames()) { + scheduleScanningIfExternalClass(thrownExceptionName, "method throws", log); + } + } } } // Check field annotations From cd9e5111350b979f33cb4dc898fb725be7063338 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Jan 2022 01:01:45 -0700 Subject: [PATCH 333/713] Return empty list / empty array if method throws no exceptions (#633) --- .../java/io/github/classgraph/MethodInfo.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index e783f59f8..b0b21f783 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -324,6 +324,11 @@ public String getTypeSignatureOrTypeDescriptorStr() { } } + /** + * Returns the list of exceptions thrown by the method, as a {@link ClassInfoList}. + * + * @return The list of exceptions thrown by the method, as a {@link ClassInfoList} (the list may be empty). + */ public ClassInfoList getThrownExceptions() { if (thrownExceptions == null && thrownExceptionNames != null) { thrownExceptions = new ClassInfoList(thrownExceptionNames.length); @@ -335,11 +340,16 @@ public ClassInfoList getThrownExceptions() { } } } - return thrownExceptions; + return thrownExceptions == null ? ClassInfoList.EMPTY_LIST : thrownExceptions; } + /** + * Returns the exceptions thrown by the method, as an array. + * + * @return The exceptions thrown by the method, as an array (the array may be empty). + */ public String[] getThrownExceptionNames() { - return thrownExceptionNames; + return thrownExceptionNames == null ? new String[0] : thrownExceptionNames; } // ------------------------------------------------------------------------------------------------------------- @@ -1168,8 +1178,8 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { if (i > 0) { buf.append(", "); } - buf.append(useSimpleNames ? ClassInfo.getSimpleName(thrownExceptionNames[i]) : - thrownExceptionNames[i].replace('$', '.')); + buf.append(useSimpleNames ? ClassInfo.getSimpleName(thrownExceptionNames[i]) + : thrownExceptionNames[i].replace('$', '.')); } } } From f63b521a5203cea79498d7f33bc4253861513848 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Jan 2022 01:02:10 -0700 Subject: [PATCH 334/713] Source > Cleanup --- src/main/java/io/github/classgraph/MethodInfo.java | 6 +++--- .../nonapi/io/github/classgraph/utils/FastPathResolver.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index b0b21f783..3eba11146 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -332,8 +332,8 @@ public String getTypeSignatureOrTypeDescriptorStr() { public ClassInfoList getThrownExceptions() { if (thrownExceptions == null && thrownExceptionNames != null) { thrownExceptions = new ClassInfoList(thrownExceptionNames.length); - for (String thrownExceptionName : thrownExceptionNames) { - ClassInfo classInfo = scanResult.getClassInfo(thrownExceptionName); + for (final String thrownExceptionName : thrownExceptionNames) { + final ClassInfo classInfo = scanResult.getClassInfo(thrownExceptionName); if (classInfo != null) { thrownExceptions.add(classInfo); classInfo.setScanResult(scanResult); @@ -959,7 +959,7 @@ protected void findReferencedClassInfo(final Map classNameToC } } if (thrownExceptionNames != null) { - ClassInfoList thrownExceptions = getThrownExceptions(); + final ClassInfoList thrownExceptions = getThrownExceptions(); if (thrownExceptions != null) { for (int i = 0; i < thrownExceptions.size(); i++) { classNameToClassInfo.put(thrownExceptionNames[i], thrownExceptions.get(i)); diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 27f17adfc..4d0853e14 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -264,7 +264,7 @@ public static String resolve(final String resolveBasePath, final String relative } } } while (matchedPrefix); - + if (isFileOrJarURL) { if (WINDOWS) { if (relativePath.startsWith("\\\\\\\\", startIdx) || relativePath.startsWith("////", startIdx)) { From 2459fdfdce4d223735e6319ff7e5c74227e8d178 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 31 Jan 2022 21:58:41 +0100 Subject: [PATCH 335/713] Fix QuarkusClassLoaderHandler on Quarkus 2.7. In Quarkus 2.7, the class loader stopped returning `JarClassPathElement` or `DirectoryClassPathElement` entries. Instead, some implementation of `ClassPathElement` is returned. This change tries to access it's `getRoot` method, returning a `java.nio.file.Path` pointing to the root of the element, as supported with class graph. The behaviour has been verified on Quarkus 2.6 and Quarkus 2.7. --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index d03cfebf3..9b7335928 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -127,6 +127,13 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "root"), classLoader, scanSpec, log); + } else { + Object rootPath = ReflectionUtils.invokeMethod(false, element, "getRoot"); + if (rootPath instanceof Path) { + classpathOrder.addClasspathEntry(ReflectionUtils.invokeMethod(false, element, "getRoot"), + classLoader, + scanSpec, log); + } } } } From d816f589d78319aec5096b577382197ba5c94322 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 02:01:23 +0000 Subject: [PATCH 336/713] Bump animal-sniffer-enforcer-rule from 1.20 to 1.21 Bumps [animal-sniffer-enforcer-rule](https://github.com/mojohaus/animal-sniffer) from 1.20 to 1.21. - [Release notes](https://github.com/mojohaus/animal-sniffer/releases) - [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.20...animal-sniffer-parent-1.21) --- updated-dependencies: - dependency-name: org.codehaus.mojo:animal-sniffer-enforcer-rule dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b9110224..1f4b9b51f 100644 --- a/pom.xml +++ b/pom.xml @@ -255,7 +255,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.20 + 1.21 From 0469353e0fa0e8620dee26315638bfd5eba6e26a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Feb 2022 02:01:37 +0000 Subject: [PATCH 337/713] Bump jvm-driver from 8.7.10 to 8.9.3 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.7.10 to 8.9.3. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.7.10...jvm-driver-8.9.3) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b9110224..aa8d3416a 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.7.10 + 8.9.3 true From f8edf1b470a655e96267d7062bad4be4366c652f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 10:23:22 -0700 Subject: [PATCH 338/713] Prevent infinite loop (#645) --- src/main/java/io/github/classgraph/MethodInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 3eba11146..e78660c3f 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -907,7 +907,9 @@ void setScanResult(final ScanResult scanResult) { } if (this.thrownExceptions != null) { for (final ClassInfo thrownException : thrownExceptions) { - thrownException.setScanResult(scanResult); + if (thrownException.scanResult == null) { // Prevent infinite loop + thrownException.setScanResult(scanResult); + } } } } From 5bf5ac2e69d77bfaa6223e4e59470dbb09263a40 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 10:39:54 -0700 Subject: [PATCH 339/713] Allow Kotlin class names with parentheses in them (#645) --- .../java/io/github/classgraph/Classfile.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index d3a39c62a..fd3f81546 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1055,10 +1055,12 @@ private List readTypePath() throws IOException { /** * Read constant pool entries. * + * @param log + * The log * @throws IOException * Signals that an I/O exception has occurred. */ - private void readConstantPoolEntries() throws IOException { + private void readConstantPoolEntries(final LogNode log) throws IOException { // Only record class dependency info if inter-class dependencies are enabled List classNameCpIdxs = null; List typeSignatureIdxs = null; @@ -1198,21 +1200,29 @@ private void readConstantPoolEntries() throws IOException { final String typeSigStr = getConstantPoolString(cpIdx); if (typeSigStr != null) { try { - if (typeSigStr.indexOf('(') >= 0 || "".equals(typeSigStr)) { - // Parse the type signature - final MethodTypeSignature typeSig = MethodTypeSignature.parse(typeSigStr, + if (typeSigStr.startsWith("L") && typeSigStr.endsWith(";")) { + // Parse the class name + final TypeSignature typeSig = TypeSignature.parse(typeSigStr, /* definingClassName = */ null); // Extract class names from type signature typeSig.findReferencedClassNames(refdClassNames); - } else { + } else if (typeSigStr.indexOf('(') >= 0 || "".equals(typeSigStr)) { // Parse the type signature - final TypeSignature typeSig = TypeSignature.parse(typeSigStr, + final MethodTypeSignature typeSig = MethodTypeSignature.parse(typeSigStr, /* definingClassName = */ null); // Extract class names from type signature typeSig.findReferencedClassNames(refdClassNames); + } else { + if (log != null) { + log.log("Could not extract referenced class names from constant pool string: " + + typeSigStr); + } } } catch (final ParseException e) { - throw new ClassfileFormatException("Could not parse type signature: " + typeSigStr, e); + if (log != null) { + log.log("Could not extract referenced class names from constant pool string: " + + typeSigStr + " : " + e); + } } } } @@ -1947,7 +1957,7 @@ public void decorate(final ClassTypeSignature classTypeSignature) { majorVersion = reader.readUnsignedShort(); // Read the constant pool - readConstantPoolEntries(); + readConstantPoolEntries(log); // Read basic class info ( readBasicClassInfo(); From 272c5bd3db8f0b317e594dfa845a062f6fe60eab Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 10:45:23 -0700 Subject: [PATCH 340/713] Source > Cleanup --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 5 ++--- .../io/github/classgraph/utils/ProxyingInputStream.java | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index 9b7335928..f00129d29 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -128,11 +128,10 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "root"), classLoader, scanSpec, log); } else { - Object rootPath = ReflectionUtils.invokeMethod(false, element, "getRoot"); + final Object rootPath = ReflectionUtils.invokeMethod(false, element, "getRoot"); if (rootPath instanceof Path) { classpathOrder.addClasspathEntry(ReflectionUtils.invokeMethod(false, element, "getRoot"), - classLoader, - scanSpec, log); + classLoader, scanSpec, log); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java index 0e24abe67..1497895d0 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java @@ -103,6 +103,7 @@ public int read(final byte[] b, final int off, final int len) throws IOException } // No @Override, since this method is not present in JDK 7 + @Override public byte[] readAllBytes() throws IOException { if (readAllBytes == null) { throw new UnsupportedOperationException(); @@ -115,6 +116,7 @@ public byte[] readAllBytes() throws IOException { } // No @Override, since this method is not present in JDK 7 + @Override public byte[] readNBytes(final int len) throws IOException { if (readNBytes1 == null) { throw new UnsupportedOperationException(); @@ -127,6 +129,7 @@ public byte[] readNBytes(final int len) throws IOException { } // No @Override, since this method is not present in JDK 7 + @Override public int readNBytes(final byte[] b, final int off, final int len) throws IOException { if (readNBytes3 == null) { throw new UnsupportedOperationException(); @@ -164,6 +167,7 @@ public long skip(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 + @Override public void skipNBytes(final long n) throws IOException { if (skipNBytes == null) { throw new UnsupportedOperationException(); @@ -176,6 +180,7 @@ public void skipNBytes(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 + @Override public long transferTo(final OutputStream out) throws IOException { if (transferTo == null) { throw new UnsupportedOperationException(); From d0c50555ed83ca1c3e34853782f9ee1730c64d3e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 10:46:25 -0700 Subject: [PATCH 341/713] Remove redundancy (#642) --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index f00129d29..b2c99c12f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -130,8 +130,7 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl } else { final Object rootPath = ReflectionUtils.invokeMethod(false, element, "getRoot"); if (rootPath instanceof Path) { - classpathOrder.addClasspathEntry(ReflectionUtils.invokeMethod(false, element, "getRoot"), - classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(rootPath, classLoader, scanSpec, log); } } } From df45ffa6ccac994838eef5539732edf6051dba98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 11:11:59 -0700 Subject: [PATCH 342/713] Also scan classpath if override classloader is AppClassLoader (#639) --- .../classgraph/classpath/ClasspathFinder.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 3cc862ce9..45972e80e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -117,6 +117,9 @@ public ClassGraphClassLoader getDelegateClassGraphClassLoader() { public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); + // Require scanning traditional classpath if an override classloader is AppClassLoader (#639) + boolean forceScanJavaClassPath = false; + // If classloaders are overridden, check if the override classloader(s) is/are JPMS classloaders. // If so, need to enable non-system module scanning. boolean scanNonSystemModules; @@ -132,23 +135,21 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // It's not possible to instantiate AppClassLoader or PlatformClassLoader, so if these are // passed in as override classloaders, they must have been obtained using // Thread.currentThread().getContextClassLoader() [.getParent()] or similar - if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader")) { + if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader") + || classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { if (!scanSpec.enableSystemJarsAndModules) { if (classpathFinderLog != null) { classpathFinderLog.log("overrideClassLoaders() was called with an instance of " - + "jdk.internal.loader.ClassLoaders$AppClassLoader, which is a system " - + "classloader, so enableSystemJarsAndModules() was called automatically"); + + classLoaderClassName + ", which is a system classloader, so " + + "enableSystemJarsAndModules() was called automatically"); } scanSpec.enableSystemJarsAndModules = true; } - } else if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { - if (!scanSpec.enableSystemJarsAndModules) { - if (classpathFinderLog != null) { - classpathFinderLog.log("overrideClassLoaders() was called with an instance of " - + "jdk.internal.loader.ClassLoaders$PlatformClassLoader, which is a system " - + "classloader, so enableSystemJarsAndModules() was called automatically"); - } - scanSpec.enableSystemJarsAndModules = true; + forceScanJavaClassPath = true; + if (classpathFinderLog != null) { + classpathFinderLog.log("overrideClassLoaders() was called with an instance of " + + classLoaderClassName + ", which is a system classloader, so the " + + "`java.lang.path` classpath will also be scanned"); } } } @@ -291,7 +292,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // and the classpath is not overridden, unless only module scanning was enabled, and an unnamed module // layer was encountered -- in this case, have to forcibly scan java.class.path, since the ModuleLayer // API doesn't allow for the opening of unnamed modules. - if ((!scanSpec.ignoreParentClassLoaders && scanSpec.overrideClassLoaders == null + if ((!scanSpec.ignoreParentClassLoaders && (scanSpec.overrideClassLoaders == null || forceScanJavaClassPath) && scanSpec.overrideClasspath == null) || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); From e21e0fea13804de434603b6e25b3e47c8de570f2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Feb 2022 11:38:30 -0700 Subject: [PATCH 343/713] JDK 7 compat fixes --- .../io/github/classgraph/utils/ProxyingInputStream.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java index 1497895d0..0e24abe67 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ProxyingInputStream.java @@ -103,7 +103,6 @@ public int read(final byte[] b, final int off, final int len) throws IOException } // No @Override, since this method is not present in JDK 7 - @Override public byte[] readAllBytes() throws IOException { if (readAllBytes == null) { throw new UnsupportedOperationException(); @@ -116,7 +115,6 @@ public byte[] readAllBytes() throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public byte[] readNBytes(final int len) throws IOException { if (readNBytes1 == null) { throw new UnsupportedOperationException(); @@ -129,7 +127,6 @@ public byte[] readNBytes(final int len) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public int readNBytes(final byte[] b, final int off, final int len) throws IOException { if (readNBytes3 == null) { throw new UnsupportedOperationException(); @@ -167,7 +164,6 @@ public long skip(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public void skipNBytes(final long n) throws IOException { if (skipNBytes == null) { throw new UnsupportedOperationException(); @@ -180,7 +176,6 @@ public void skipNBytes(final long n) throws IOException { } // No @Override, since this method is not present in JDK 7 - @Override public long transferTo(final OutputStream out) throws IOException { if (transferTo == null) { throw new UnsupportedOperationException(); From 5138b5ee3a9c6d610ae7aabae8485fda9275620b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 13 Feb 2022 16:31:12 -0700 Subject: [PATCH 344/713] [maven-release-plugin] prepare release classgraph-4.8.139 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fb33036dc..440744eb8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.139-SNAPSHOT + 4.8.139 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.139 From f874380e58ac3e9e4834df199129a58c79d15f33 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 13 Feb 2022 16:31:16 -0700 Subject: [PATCH 345/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 440744eb8..ff3f2cb7d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.139 + 4.8.140-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.139 + HEAD From b8d71353bbfc28ab2b5a6c1642ade169ccc47a52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 02:01:58 +0000 Subject: [PATCH 346/713] Bump maven-javadoc-plugin from 3.3.1 to 3.3.2 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.1...maven-javadoc-plugin-3.3.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff3f2cb7d..7e7be31d4 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.1 + 3.3.2 org.apache.maven.plugins From 7afa63b75efe008c582e9e46cb36b3e895d3998d Mon Sep 17 00:00:00 2001 From: Arthur Hupka-Merle Date: Tue, 15 Feb 2022 16:25:10 +0100 Subject: [PATCH 347/713] #651 fixes NPE in JbossClassLoaderHandler --- .../classloaderhandler/JBossClassLoaderHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index f25a50f9b..2e5499696 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.io.File; import java.lang.reflect.Array; import java.nio.file.Path; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -212,7 +213,9 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class @SuppressWarnings("unchecked") final Map moduleMap = (Map) ReflectionUtils.getFieldVal(false, callerModuleLoader, "moduleMap"); - for (final Entry ent : moduleMap.entrySet()) { + Set> moduleMapEntries = + moduleMap != null ? moduleMap.entrySet() : Collections.>emptySet(); + for (final Entry ent : moduleMapEntries) { // type FutureModule final Object val = ent.getValue(); // type Module From c1a692f97814763973997fd3d3898a530baae1d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 02:01:33 +0000 Subject: [PATCH 348/713] Bump maven-site-plugin from 3.10.0 to 3.11.0 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.10.0...maven-site-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-site-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff3f2cb7d..a79a39459 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ org.apache.maven.plugins maven-site-plugin - 3.10.0 + 3.11.0 From afc80afaf5e598b6e9e5f726004875bec66e58de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Feb 2022 02:01:32 +0000 Subject: [PATCH 349/713] Bump nexus-staging-maven-plugin from 1.6.8 to 1.6.12 Bumps nexus-staging-maven-plugin from 1.6.8 to 1.6.12. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff3f2cb7d..e26d16e80 100644 --- a/pom.xml +++ b/pom.xml @@ -214,7 +214,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.12 org.apache.maven.plugins From 052270d20435979f51e48ee19b99d30dcd10fc48 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 24 Feb 2022 03:14:41 -0700 Subject: [PATCH 350/713] [maven-release-plugin] prepare release classgraph-4.8.140 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ff3f2cb7d..bb01db861 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.140-SNAPSHOT + 4.8.140 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD + classgraph-4.8.140 From fd8b7cfbef6cac91d279d987417002dc1e4e0fef Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 24 Feb 2022 03:15:01 -0700 Subject: [PATCH 351/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb01db861..f777d6fb4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.140 + 4.8.141-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 151aa07d7eaf42ab99af05efceb073409546115f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 24 Feb 2022 21:48:39 -0700 Subject: [PATCH 352/713] Update README.md Add OSPB award --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b06f01d08..8508a9801 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ClassGraph -ClassGraph Logo       Duke Award Logo +ClassGraph Logo       Duke Award logo       Google Open Source Peer Bonus logo ClassGraph is an uber-fast parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. From 53b4434c7df314a5a8fd0b1aa54889b441b01f2b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 24 Feb 2022 21:49:39 -0700 Subject: [PATCH 353/713] Update README.md Fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8508a9801..5c362b604 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ClassGraph -ClassGraph Logo       Duke Award logo       Google Open Source Peer Bonus logo +ClassGraph Logo       Duke Award logo       Google Open Source Peer Bonus logo ClassGraph is an uber-fast parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. From 6f507c2ac0c2c11a7114126826adbf32fd80df99 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 24 Feb 2022 21:50:39 -0700 Subject: [PATCH 354/713] Update README.md Fix spacing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c362b604..affee553c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ClassGraph -ClassGraph Logo       Duke Award logo       Google Open Source Peer Bonus logo +ClassGraph Logo       Duke Award logo     Google Open Source Peer Bonus logo ClassGraph is an uber-fast parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. From ba7c375d4f6542e372f07d0e8db926c2472f9f30 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Feb 2022 09:00:21 -0700 Subject: [PATCH 355/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index affee553c..895a67817 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ClassGraph is an uber-fast parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. -| _ClassGraph won a Duke's Choice Award (a recognition of the most useful and/or innovative software in the Java ecosystem) at Oracle Code One 2018._ Thanks to all the users who have reported bugs, requested features, offered suggestions, and submitted pull requests to help get ClassGraph to where it is today. | +| _ClassGraph won a Duke's Choice Award (a recognition of the most useful and/or innovative software in the Java ecosystem) at Oracle Code One 2018, and a Google Open Source Peer Bonus award in 2022._ Thanks to all the users who have reported bugs, requested features, offered suggestions, and submitted pull requests to help get ClassGraph to where it is today. | |-----------------------------| [![Platforms: Windows, Mac OS X, Linux, Android (build-time)](https://img.shields.io/badge/platforms-Windows,_Mac_OS_X,_Linux,_Android_(build--time)-blue.svg)](#) From 9ea553a566a29a17e9994c1ecbd432c473ae474c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Feb 2022 10:20:32 -0700 Subject: [PATCH 356/713] Make URL handling more robust (#625) --- .../classgraph/ClasspathElementModule.java | 3 +- .../classgraph/ClasspathElementZip.java | 3 +- .../java/io/github/classgraph/Scanner.java | 64 ++++++++++--------- .../JBossClassLoaderHandler.java | 6 +- .../fastzipfilereader/NestedJarHandler.java | 4 +- 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 7ea8abf9f..23e8e9f4a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -115,7 +115,8 @@ void open(final WorkQueue workQueueIgnored, final LogNod moduleReaderProxyRecycler = moduleRefToModuleReaderProxyRecyclerMap.get(moduleRef, log); } catch (final IOException | NullSingletonException | NewInstanceException e) { if (log != null) { - log(classpathElementIdx, "Skipping invalid module " + getModuleName() + " : " + e, log); + log(classpathElementIdx, "Skipping invalid module " + getModuleName() + " : " + + (e.getCause() == null ? e : e.getCause()), log); } skipClasspathElement = true; return; diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index f1a632122..2fd9e7bfa 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -164,7 +164,8 @@ void open(final WorkQueue workQueue, final LogNode log) } catch (final NullSingletonException | NewInstanceException e) { // Generally thrown on the second and subsequent attempt to call .get(), after the first failed, // or newInstance() threw an exception - throw new IOException("Could not get logical zipfile " + rawPath + " : " + e); + throw new IOException("Could not get logical zipfile " + rawPath + " : " + + (e.getCause() == null ? e : e.getCause())); } logicalZipFile = logicalZipFileAndPackageRoot.getKey(); if (logicalZipFile == null) { diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 44ef5aa99..13d9d0ea6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -35,8 +35,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLDecoder; import java.nio.file.FileSystemNotFoundException; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; @@ -411,18 +411,8 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa // Check type of classpath entry object Path classpathEntryPath = null; if (classpathEntryObj instanceof URL) { - URL classpathEntryURL = (URL) classpathEntryObj; - String scheme = classpathEntryURL.getProtocol(); - if ("jar".equals(scheme)) { - // Strip off "jar:" scheme prefix - try { - classpathEntryURL = new URL( - URLDecoder.decode(classpathEntryURL.toString(), "UTF-8").substring(4)); - scheme = classpathEntryURL.getProtocol(); - } catch (final MalformedURLException e) { - throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); - } - } + final URL classpathEntryURL = (URL) classpathEntryObj; + final String scheme = classpathEntryURL.getProtocol(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, @@ -432,8 +422,21 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa // See if the URL resolves to a file or directory via the Path API classpathEntryPath = Paths.get(classpathEntryURL.toURI()); } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { - throw new IOException( - "Cannot handle URL " + classpathEntryURL + " : " + e.getMessage()); + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) + try { + classpathEntryPath = new File(classpathEntryURL.toURI()).toPath(); + } catch (IllegalArgumentException | URISyntaxException e2) { + // Try normalizing path (which would reduce this to simply "myjar.jar") + // and then passing it again to Paths.get. + try { + classpathEntryPath = Paths + .get(FastPathResolver.resolve(classpathEntryURL.toString())); + } catch (final IllegalArgumentException | SecurityException e3) { + throw new IOException( + "Cannot handle URL " + classpathEntryURL + " : " + e3.getMessage()); + } + } } catch (final FileSystemNotFoundException e) { // This is a custom URL scheme without a backing FileSystem return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, @@ -443,16 +446,6 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } else if (classpathEntryObj instanceof URI) { URI classpathEntryURI = (URI) classpathEntryObj; String scheme = classpathEntryURI.getScheme(); - if ("jar".equals(scheme)) { - // Strip off "jar:" scheme prefix - try { - classpathEntryURI = new URI( - URLDecoder.decode(classpathEntryURI.toString(), "UTF-8").substring(4)); - scheme = classpathEntryURI.getScheme(); - } catch (final URISyntaxException e) { - throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); - } - } if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, @@ -462,8 +455,21 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa // See if the URI resolves to a file or directory via the Path API classpathEntryPath = Paths.get(classpathEntryURI); } catch (final IllegalArgumentException | SecurityException e) { - throw new IOException( - "Cannot handle URI " + classpathEntryURI + " : " + e.getMessage()); + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) + try { + classpathEntryPath = new File(classpathEntryURI).toPath(); + } catch (IllegalArgumentException e2) { + // Try normalizing path (which would reduce this to simply "myjar.jar") + // and then passing it again to Paths.get. + try { + classpathEntryPath = Paths + .get(FastPathResolver.resolve(classpathEntryURI.toString())); + } catch (final IllegalArgumentException | SecurityException e3) { + throw new IOException( + "Cannot handle URI " + classpathEntryURI + " : " + e3.getMessage()); + } + } } catch (final FileSystemNotFoundException e) { // This is a custom URI scheme without a backing FileSystem return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, @@ -543,7 +549,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa dirOrPathPackageRoot, classpathEntry.classLoader), log); } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for canonical path " - + canonicalPathNormalized + " : " + e); + + canonicalPathNormalized + " : " + (e.getCause() == null ? e : e.getCause())); } } else { // Otherwise path is already canonical, and this is the first time this path has @@ -585,7 +591,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, log); } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for classpath entry " - + workUnit.rawClasspathEntry + " : " + e); + + workUnit.rawClasspathEntry + " : " + (e.getCause() == null ? e : e.getCause())); } // Only run open() once per ClasspathElement (it is possible for there to be diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 2e5499696..6f31cd9b3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -213,9 +213,9 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class @SuppressWarnings("unchecked") final Map moduleMap = (Map) ReflectionUtils.getFieldVal(false, callerModuleLoader, "moduleMap"); - Set> moduleMapEntries = - moduleMap != null ? moduleMap.entrySet() : Collections.>emptySet(); - for (final Entry ent : moduleMapEntries) { + final Set> moduleMapEntries = moduleMap != null ? moduleMap.entrySet() + : Collections.> emptySet(); + for (final Entry ent : moduleMapEntries) { // type FutureModule final Object val = ent.getValue(); // type Module diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index f3043e243..d836e5230 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -190,8 +190,8 @@ public Entry newInstance(final String nestedJarPathRaw, physicalZipFile = canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); } catch (final NullSingletonException | NewInstanceException e) { // If getting PhysicalZipFile failed, re-wrap in IOException - throw new IOException( - "Could not get PhysicalZipFile for path " + nestedJarPath + " : " + e); + throw new IOException("Could not get PhysicalZipFile for path " + nestedJarPath + + " : " + (e.getCause() == null ? e : e.getCause())); } catch (final SecurityException e) { // getCanonicalFile() failed (it may have also failed with IOException) throw new IOException( From 0d0fb6630004348f276f28997bcc4605bc61114b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Feb 2022 10:22:43 -0700 Subject: [PATCH 357/713] [maven-release-plugin] prepare release classgraph-4.8.141 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f777d6fb4..cb21660df 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.141-SNAPSHOT + 4.8.141 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.140 + classgraph-4.8.141 From 6a1cb30f59cf8d1a15e7ecda89000cfcbd99d3df Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Feb 2022 10:22:47 -0700 Subject: [PATCH 358/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cb21660df..38f2754ff 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.141 + 4.8.142-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.141 + classgraph-4.8.140 From 19ed793b00d2706f20f56e4b586775df4017127e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 02:02:14 +0000 Subject: [PATCH 359/713] Bump jvm-driver from 8.9.3 to 8.9.6 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.9.3 to 8.9.6. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.9.3...jvm-driver-8.9.6) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38f2754ff..659c5005b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.9.3 + 8.9.6 true From 968401ae6db3c8d3b96a782e4083fb08eec97846 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 03:02:13 +0000 Subject: [PATCH 360/713] Bump actions/checkout from 2.4.0 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3c34e619..90d58c6d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - name: Set up JDK uses: actions/setup-java@v2 with: From ff9535c4e1fd871d482ab91b35e829626e7af0cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Mar 2022 02:01:07 +0000 Subject: [PATCH 361/713] Bump maven-compiler-plugin from 3.9.0 to 3.10.1 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.9.0 to 3.10.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.9.0...maven-compiler-plugin-3.10.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38f2754ff..e3fac01dd 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.9.0 + 3.10.1 org.apache.maven.plugins From 3e1ede630c0fd8ae66eefdff3216054d2ac35a81 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 16 Mar 2022 20:20:12 -0600 Subject: [PATCH 362/713] Don't use SecurityManager reflective method in JDK9+ (#663) --- .../classgraph/classpath/CallStackReader.java | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index 7fb56dfd1..a60c71ae6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -143,18 +143,20 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { static Class[] getClassContext(final LogNode log) { if (callStack == null) { // For JRE 9+, use StackWalker to get call stack. - // N.B. need to work around StackWalker bug fixed in JDK 13, and backported to 12.0.2 and 11.0.4 - // (probably introduced in JDK 9, when StackWalker was introduced): - // https://github.com/classgraph/classgraph/issues/341 - // https://bugs.openjdk.java.net/browse/JDK-8210457 - if ((VersionFinder.JAVA_MAJOR_VERSION == 11 - && (VersionFinder.JAVA_MINOR_VERSION >= 1 || VersionFinder.JAVA_SUB_VERSION >= 4) - && !VersionFinder.JAVA_IS_EA_VERSION) - || (VersionFinder.JAVA_MAJOR_VERSION == 12 - && (VersionFinder.JAVA_MINOR_VERSION >= 1 || VersionFinder.JAVA_SUB_VERSION >= 2) - && !VersionFinder.JAVA_IS_EA_VERSION) - || (VersionFinder.JAVA_MAJOR_VERSION == 13 && !VersionFinder.JAVA_IS_EA_VERSION) - || VersionFinder.JAVA_MAJOR_VERSION > 13) { + if (VersionFinder.JAVA_MAJOR_VERSION == 9 // + || VersionFinder.JAVA_MAJOR_VERSION == 10 // + || (VersionFinder.JAVA_MAJOR_VERSION == 11 // + && VersionFinder.JAVA_MINOR_VERSION == 0 && VersionFinder.JAVA_SUB_VERSION < 4) + || (VersionFinder.JAVA_MAJOR_VERSION == 12 && VersionFinder.JAVA_MINOR_VERSION == 0 + && VersionFinder.JAVA_SUB_VERSION < 2)) { + // Don't trigger the StackWalker bug that crashed the JVM, which was fixed in JDK 13, + // and backported to 12.0.2 and 11.0.4 (probably introduced in JDK 9, when StackWalker + // was introduced): + // https://github.com/classgraph/classgraph/issues/341 + // https://bugs.openjdk.java.net/browse/JDK-8210457 + // -- fall through + } else { + // Get the stack via StackWalker. // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html try { @@ -169,8 +171,9 @@ public Class[] call() throws Exception { } } - // For JRE 7 and 8, use SecurityManager to get call stack - if (callStack == null || callStack.length == 0) { + // For JRE 7 and 8, use SecurityManager to get call stack (don't use this method on JDK 9+, + // because it will result in a reflective illegal access warning, see #663) + if (VersionFinder.JAVA_MAJOR_VERSION < 9 && (callStack == null || callStack.length == 0)) { try { callStack = ReflectionUtils.doPrivileged(new Callable[]>() { @Override @@ -185,9 +188,15 @@ public Class[] call() throws Exception { // As a fallback, use getStackTrace() to try to get the call stack if (callStack == null || callStack.length == 0) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + StackTraceElement[] stackTrace = null; + try { + stackTrace = Thread.currentThread().getStackTrace(); + } catch (final SecurityException e) { + // Fall through + } if (stackTrace == null || stackTrace.length == 0) { try { + // Try getting stacktrace by throwing an exception throw new Exception(); } catch (final Exception e) { stackTrace = e.getStackTrace(); @@ -203,12 +212,15 @@ public Class[] call() throws Exception { } if (!stackClassesList.isEmpty()) { callStack = stackClassesList.toArray(new Class[0]); - } else { - // Last-ditch effort -- include just this class in the call stack - callStack = new Class[] { CallStackReader.class }; } } } + + // Last-ditch effort -- include just this class in the call stack + if (callStack == null || callStack.length == 0) { + callStack = new Class[] { CallStackReader.class }; + } + return callStack; } } From 9036017b3132600ce61fd154f282290de7e825ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 16 Mar 2022 20:24:33 -0600 Subject: [PATCH 363/713] Also reject `-ea` versions before the final JVM fix (#663) --- .../io/github/classgraph/classpath/CallStackReader.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index a60c71ae6..7bd7c0cc5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -146,9 +146,12 @@ static Class[] getClassContext(final LogNode log) { if (VersionFinder.JAVA_MAJOR_VERSION == 9 // || VersionFinder.JAVA_MAJOR_VERSION == 10 // || (VersionFinder.JAVA_MAJOR_VERSION == 11 // - && VersionFinder.JAVA_MINOR_VERSION == 0 && VersionFinder.JAVA_SUB_VERSION < 4) + && VersionFinder.JAVA_MINOR_VERSION == 0 + && (VersionFinder.JAVA_SUB_VERSION < 4 + || (VersionFinder.JAVA_SUB_VERSION == 4 && VersionFinder.JAVA_IS_EA_VERSION))) || (VersionFinder.JAVA_MAJOR_VERSION == 12 && VersionFinder.JAVA_MINOR_VERSION == 0 - && VersionFinder.JAVA_SUB_VERSION < 2)) { + && (VersionFinder.JAVA_SUB_VERSION < 2 || (VersionFinder.JAVA_SUB_VERSION == 2 + && VersionFinder.JAVA_IS_EA_VERSION)))) { // Don't trigger the StackWalker bug that crashed the JVM, which was fixed in JDK 13, // and backported to 12.0.2 and 11.0.4 (probably introduced in JDK 9, when StackWalker // was introduced): From 590076704d673c02c7a33e215fbc701ec0ef2747 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Mar 2022 02:01:41 +0000 Subject: [PATCH 364/713] Bump slf4j-api from 2.0.0-alpha6 to 2.0.0-alpha7 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha6 to 2.0.0-alpha7. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha6...v_2.0.0-alpha7) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc2a907fb..913a49cfb 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.0-alpha6 + 2.0.0-alpha7 test From ff7e1fad7de4c5b8bcae342b6ac52e6e34ff16f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Mar 2022 02:01:44 +0000 Subject: [PATCH 365/713] Bump slf4j-jdk14 from 2.0.0-alpha6 to 2.0.0-alpha7 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha6 to 2.0.0-alpha7. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha6...v_2.0.0-alpha7) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc2a907fb..4b224cded 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha6 + 2.0.0-alpha7 test From 558056e15aa639ab077940188c1e320e191ef975 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 19:00:53 -0600 Subject: [PATCH 366/713] Test for #664 --- .../features/ClassTypeAnnotation.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java new file mode 100644 index 000000000..5c34aec8b --- /dev/null +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -0,0 +1,48 @@ +package io.github.classgraph.features; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +/** + * Test + */ +class ClassTypeAnnotation { + /***/ + @Retention(RetentionPolicy.RUNTIME) + private static @interface X { + } + + /***/ + private static class Z { + } + + /***/ + private static class Y extends @X Z { + } + + @Test + void classTypeAnnotation() { + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(ClassTypeAnnotation.class.getPackage().getName()).enableAllInfo().scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); + assertThat(classInfo).isNotNull(); + + // This is + // Y extends ClassTypeAnnotation.@X Z + // and not + // Y extends @X ClassTypeAnnotation.Z + // Because the annotation is on Z, not ClassTypeAnnotation + assertThat(classInfo.getTypeSignature().toString()).isEqualTo("private static class " + + Y.class.getName() + " extends " + ClassTypeAnnotation.class.getName() + ".@" + + X.class.getName() + " " + Z.class.getSimpleName()); + } + } +} From 01ed6f2c351ff209f86d6c9a33f0aaf745bd21f7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 19:50:36 -0600 Subject: [PATCH 367/713] Consistently use `$` rather than `.` for inner classes --- .../classgraph/ClassRefTypeSignature.java | 16 +++---- .../java/io/github/classgraph/MethodInfo.java | 2 +- .../java/io/github/classgraph/Scanner.java | 7 ++- .../features/ClassTypeAnnotation.java | 17 +++++-- .../issues/GenericInnerClassTypedField.java | 4 +- .../issues/issue152/Issue152Test.java | 7 ++- .../issues/issue175/Issue175Test.java | 44 +++++++++---------- .../issues/issue402/TypeAnnotationTest.java | 16 +++---- .../test/methodinfo/MethodInfoTest.java | 25 ++++++----- 9 files changed, 71 insertions(+), 67 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 9ba580361..16019792a 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -408,19 +408,13 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn // Append suffixes if (!suffixes.isEmpty()) { for (int i = useSimpleNames ? suffixes.size() - 1 : 0; i < suffixes.size(); i++) { - final AnnotationInfoList typeAnnotations = suffixTypeAnnotations == null ? null - : suffixTypeAnnotations.get(i); if (!useSimpleNames) { - // Use '.' before each suffix in the toString() representation, since that is - // how the class name will be shown in Java, e.g. OuterClass.InnerClass; - // however, use '$' if the class name is numerical, i.e. "...$1" for anonymous - // inner classes. - if (Character.isDigit(suffixes.get(i).charAt(0))) { - buf.append('$'); - } else { - buf.append('.'); - } + // Use '$' rather than '.' as separator for suffixes, since that is what Class.getName() does. + buf.append('$'); } + final AnnotationInfoList typeAnnotations = suffixTypeAnnotations == null ? null + : suffixTypeAnnotations.get(i); + // Append type annotations for this suffix if (typeAnnotations != null && !typeAnnotations.isEmpty()) { for (final AnnotationInfo annotationInfo : typeAnnotations) { annotationInfo.toString(useSimpleNames, buf); diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index e78660c3f..0eb8fe03c 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -1181,7 +1181,7 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { buf.append(", "); } buf.append(useSimpleNames ? ClassInfo.getSimpleName(thrownExceptionNames[i]) - : thrownExceptionNames[i].replace('$', '.')); + : thrownExceptionNames[i]); } } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 13d9d0ea6..07536e41d 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -36,7 +36,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystemNotFoundException; -import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; @@ -444,8 +443,8 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } } } else if (classpathEntryObj instanceof URI) { - URI classpathEntryURI = (URI) classpathEntryObj; - String scheme = classpathEntryURI.getScheme(); + final URI classpathEntryURI = (URI) classpathEntryObj; + final String scheme = classpathEntryURI.getScheme(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, @@ -459,7 +458,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa // for paths like "jar:file:myjar.jar!/" (#625) try { classpathEntryPath = new File(classpathEntryURI).toPath(); - } catch (IllegalArgumentException e2) { + } catch (final IllegalArgumentException e2) { // Try normalizing path (which would reduce this to simply "myjar.jar") // and then passing it again to Paths.get. try { diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java index 5c34aec8b..651ee42f5 100644 --- a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -25,7 +25,15 @@ private static class Z { } /***/ - private static class Y extends @X Z { + private static interface A { + } + + /***/ + private static interface B { + } + + /***/ + private static class Y extends @X Z implements A, B { } @Test @@ -34,15 +42,16 @@ void classTypeAnnotation() { .acceptPackages(ClassTypeAnnotation.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); assertThat(classInfo).isNotNull(); - + // This is // Y extends ClassTypeAnnotation.@X Z // and not // Y extends @X ClassTypeAnnotation.Z // Because the annotation is on Z, not ClassTypeAnnotation assertThat(classInfo.getTypeSignature().toString()).isEqualTo("private static class " - + Y.class.getName() + " extends " + ClassTypeAnnotation.class.getName() + ".@" - + X.class.getName() + " " + Z.class.getSimpleName()); + + Y.class.getName() + " extends " + ClassTypeAnnotation.class.getName() + "$@" + + X.class.getName() + " " + Z.class.getSimpleName() + " implements " + A.class.getName() + ", " + + B.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java index dc23ccbc9..ad08d5c66 100644 --- a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java +++ b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java @@ -44,8 +44,8 @@ public void testGenericInnerClassTypedField() { .getFieldInfo(); final ClassRefTypeSignature classRefTypeSignature = (ClassRefTypeSignature) fields.get(0) .getTypeSignature(); - assertThat(classRefTypeSignature.toString()).isEqualTo(A.class.getName().replace('$', '.') + "<" - + Integer.class.getName() + ", " + String.class.getName() + ">.B"); + assertThat(classRefTypeSignature.toString()).isEqualTo( + A.class.getName() + "<" + Integer.class.getName() + ", " + String.class.getName() + ">$B"); assertThat(classRefTypeSignature.getFullyQualifiedClassName()).isEqualTo(A.class.getName() + "$B"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java index 17c4b1be5..ff5bdac79 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -102,10 +102,9 @@ public void issue152Test() { + "final java.util.Map> param2, " + "final double[][][] param3, final int param4, final " - + TestType.class.getName().replace('$', '.') + "[] param5, " - + "final java.util.Set param6, " + "final java.util.List param7, " + + TestType.class.getName() + "[] param5, " + "final java.util.Set param6, " + "final java.util.List param7, " + "final java.util.Map param8, " + "final java.util.Set[] param9)"); assertThat(classInfo // diff --git a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java index b2d0ebc3c..d3020717b 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -181,34 +181,34 @@ public void testResultTypesNotReconciled2() { assertThat(methods).containsOnly("public final int getNextNodeId()", "private final void setNextNodeId(int )", "@org.jetbrains.annotations.NotNull public final net.corda.testing.node.InMemoryMessagingNetwork getMessagingNetwork()", - "@org.jetbrains.annotations.NotNull public final java.util.List getNodes()", - "@org.jetbrains.annotations.NotNull public final java.util.List> getNotaryNodes()", - "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode getDefaultNotaryNode()", + "@org.jetbrains.annotations.NotNull public final java.util.List getNodes()", + "@org.jetbrains.annotations.NotNull public final java.util.List> getNotaryNodes()", + "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode getDefaultNotaryNode()", "@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getDefaultNotaryIdentity()", "@org.jetbrains.annotations.NotNull public final net.corda.core.identity.PartyAndCertificate getDefaultNotaryIdentityAndCert()", "private final java.util.List generateNotaryIdentities()", - "@org.jetbrains.annotations.NotNull public java.util.List> createNotaries$node_driver_main()", - "@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork.MockNode createUnstartedNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters)", - "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.testing.node.MockNetwork.MockNode createUnstartedNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, int, java.lang.Object)", - "@org.jetbrains.annotations.NotNull public final N createUnstartedNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 nodeFactory)", - "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.testing.node.MockNetwork.MockNode createUnstartedNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, kotlin.jvm.functions.Function1, int, java.lang.Object)", - "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters)", + "@org.jetbrains.annotations.NotNull public java.util.List> createNotaries$node_driver_main()", + "@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode createUnstartedNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters)", + "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.testing.node.MockNetwork$MockNode createUnstartedNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, int, java.lang.Object)", + "@org.jetbrains.annotations.NotNull public final N createUnstartedNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 nodeFactory)", + "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.testing.node.MockNetwork$MockNode createUnstartedNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, kotlin.jvm.functions.Function1, int, java.lang.Object)", + "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters)", "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.node.internal.StartedNode createNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, int, java.lang.Object)", - "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 nodeFactory)", + "@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(@org.jetbrains.annotations.NotNull net.corda.testing.node.MockNodeParameters parameters, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 nodeFactory)", "@org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.node.internal.StartedNode createNode$default(net.corda.testing.node.MockNetwork, net.corda.testing.node.MockNodeParameters, kotlin.jvm.functions.Function1, int, java.lang.Object)", - "private final N createNodeImpl(net.corda.testing.node.MockNodeParameters parameters, kotlin.jvm.functions.Function1 nodeFactory, boolean start)", + "private final N createNodeImpl(net.corda.testing.node.MockNodeParameters parameters, kotlin.jvm.functions.Function1 nodeFactory, boolean start)", "@org.jetbrains.annotations.NotNull public final java.nio.file.Path baseDirectory(int nodeId)", "@kotlin.jvm.JvmOverloads public final void runNetwork(int rounds)", "@kotlin.jvm.JvmOverloads public static synthetic bridge void runNetwork$default(net.corda.testing.node.MockNetwork, int, int, java.lang.Object)", "@kotlin.jvm.JvmOverloads public final void runNetwork()", - "@kotlin.jvm.JvmOverloads @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode(@org.jetbrains.annotations.Nullable net.corda.core.identity.CordaX500Name legalName)", + "@kotlin.jvm.JvmOverloads @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode(@org.jetbrains.annotations.Nullable net.corda.core.identity.CordaX500Name legalName)", "@kotlin.jvm.JvmOverloads @org.jetbrains.annotations.NotNull public static synthetic bridge net.corda.node.internal.StartedNode createPartyNode$default(net.corda.testing.node.MockNetwork, net.corda.core.identity.CordaX500Name, int, java.lang.Object)", - "@kotlin.jvm.JvmOverloads @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode()", - "@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork.MockNode addressToNode(@org.jetbrains.annotations.NotNull net.corda.core.messaging.MessageRecipients msgRecipient)", + "@kotlin.jvm.JvmOverloads @org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createPartyNode()", + "@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode addressToNode(@org.jetbrains.annotations.NotNull net.corda.core.messaging.MessageRecipients msgRecipient)", "public final void startNodes()", "public final void stopNodes()", "public final void waitQuiescent()", - "public (@org.jetbrains.annotations.NotNull java.util.List cordappPackages, @org.jetbrains.annotations.NotNull net.corda.testing.node.MockNetworkParameters defaultParameters, boolean networkSendManuallyPumped, boolean threadPerNode, @org.jetbrains.annotations.NotNull net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy servicePeerAllocationStrategy, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 defaultFactory, boolean initialiseSerialization, @org.jetbrains.annotations.NotNull java.util.List notarySpecs)", - "public synthetic (java.util.List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker)", + "public (@org.jetbrains.annotations.NotNull java.util.List cordappPackages, @org.jetbrains.annotations.NotNull net.corda.testing.node.MockNetworkParameters defaultParameters, boolean networkSendManuallyPumped, boolean threadPerNode, @org.jetbrains.annotations.NotNull net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy servicePeerAllocationStrategy, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 defaultFactory, boolean initialiseSerialization, @org.jetbrains.annotations.NotNull java.util.List notarySpecs)", + "public synthetic (java.util.List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker)", "@kotlin.jvm.JvmOverloads public (@org.jetbrains.annotations.NotNull java.util.List cordappPackages, @org.jetbrains.annotations.NotNull net.corda.testing.node.MockNetworkParameters parameters)", "@kotlin.jvm.JvmOverloads public synthetic (java.util.List, net.corda.testing.node.MockNetworkParameters, int, kotlin.jvm.internal.DefaultConstructorMarker)", "@kotlin.jvm.JvmOverloads public (@org.jetbrains.annotations.NotNull java.util.List)", @@ -217,7 +217,7 @@ public void testResultTypesNotReconciled2() { "@org.jetbrains.annotations.NotNull public static final synthetic java.util.List access$getCordappPackages$p(net.corda.testing.node.MockNetwork)", "@org.jetbrains.annotations.NotNull public static final synthetic org.apache.activemq.artemis.utils.ReusableLatch access$getBusyLatch$p(net.corda.testing.node.MockNetwork)", "@org.jetbrains.annotations.NotNull public static final synthetic java.util.concurrent.atomic.AtomicInteger access$getSharedUserCount$p(net.corda.testing.node.MockNetwork)", - "@org.jetbrains.annotations.NotNull public static final synthetic net.corda.testing.node.MockNetwork.sharedServerThread$1 access$getSharedServerThread$p(net.corda.testing.node.MockNetwork)"); + "@org.jetbrains.annotations.NotNull public static final synthetic net.corda.testing.node.MockNetwork$sharedServerThread$1 access$getSharedServerThread$p(net.corda.testing.node.MockNetwork)"); } } @@ -242,8 +242,8 @@ public void testAttributeParameterMismatch() { } assertThat(methods).containsOnly( // "protected (synthetic java.lang.String $enum$name, synthetic int $enum$ordinal, @org.jetbrains.annotations.NotNull java.lang.String columnName)", - "public static net.corda.core.node.services.vault.AttachmentSort.AttachmentSortAttribute[] values()", - "public static net.corda.core.node.services.vault.AttachmentSort.AttachmentSortAttribute valueOf(java.lang.String)", + "public static net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute[] values()", + "public static net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute valueOf(java.lang.String)", "@org.jetbrains.annotations.NotNull public final java.lang.String getColumnName()"); } } @@ -272,8 +272,8 @@ public void testResultTypeReconciliationIssue() { "@org.jetbrains.annotations.NotNull public final java.util.Map> getMethodParamNames()", "@org.jetbrains.annotations.NotNull public java.util.List paramNamesFromMethod(@org.jetbrains.annotations.NotNull java.lang.reflect.Method method)", "@org.jetbrains.annotations.NotNull public java.util.List paramNamesFromConstructor(@org.jetbrains.annotations.NotNull java.lang.reflect.Constructor ctor)", - "@org.jetbrains.annotations.NotNull public final net.corda.client.jackson.StringToMethodCallParser.ParsedMethodCall parse(@org.jetbrains.annotations.Nullable T target, @org.jetbrains.annotations.NotNull java.lang.String command) throws net.corda.client.jackson.StringToMethodCallParser.UnparseableCallException", - "@org.jetbrains.annotations.NotNull public final java.lang.Object[] parseArguments(@org.jetbrains.annotations.NotNull java.lang.String methodNameHint, @org.jetbrains.annotations.NotNull java.util.List>> parameters, @org.jetbrains.annotations.NotNull java.lang.String args) throws net.corda.client.jackson.StringToMethodCallParser.UnparseableCallException", + "@org.jetbrains.annotations.NotNull public final net.corda.client.jackson.StringToMethodCallParser$ParsedMethodCall parse(@org.jetbrains.annotations.Nullable T target, @org.jetbrains.annotations.NotNull java.lang.String command) throws net.corda.client.jackson.StringToMethodCallParser$UnparseableCallException", + "@org.jetbrains.annotations.NotNull public final java.lang.Object[] parseArguments(@org.jetbrains.annotations.NotNull java.lang.String methodNameHint, @org.jetbrains.annotations.NotNull java.util.List>> parameters, @org.jetbrains.annotations.NotNull java.lang.String args) throws net.corda.client.jackson.StringToMethodCallParser$UnparseableCallException", "@org.jetbrains.annotations.NotNull public final java.util.Map getAvailableCommands()", "@kotlin.jvm.JvmOverloads public (@org.jetbrains.annotations.NotNull java.lang.Class targetType, @org.jetbrains.annotations.NotNull com.fasterxml.jackson.databind.ObjectMapper om)", "@kotlin.jvm.JvmOverloads public synthetic (java.lang.Class, com.fasterxml.jackson.databind.ObjectMapper, int, kotlin.jvm.internal.DefaultConstructorMarker)", @@ -304,7 +304,7 @@ public void testParameterArityMismatch() { } } assertThat(methods).containsOnly( - "@org.jetbrains.annotations.NotNull public static , P extends net.corda.core.node.services.vault.BaseQueryCriteriaParser, S extends net.corda.core.node.services.vault.BaseSort> java.util.Collection visit(@org.jetbrains.annotations.NotNull net.corda.core.node.services.vault.GenericQueryCriteria.ChainableQueryCriteria.AndVisitor, P parser)"); + "@org.jetbrains.annotations.NotNull public static , P extends net.corda.core.node.services.vault.BaseQueryCriteriaParser, S extends net.corda.core.node.services.vault.BaseSort> java.util.Collection visit(@org.jetbrains.annotations.NotNull net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$AndVisitor, P parser)"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java index 0d43ef8dc..5709d4c0a 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -164,27 +164,27 @@ void typeAnnotations() { .isEqualTo("@A List<@B Comparable<@F Object @C [] @D [] @E []>> comparable"); final FieldInfo inner1Field = classInfo.getFieldInfo("inner1"); - assertThat(shortNames(inner1Field)).isEqualTo("@A Outer.@B Middle.@C Inner1 inner1"); + assertThat(shortNames(inner1Field)).isEqualTo("@A Outer$@B Middle$@C Inner1 inner1"); assertThat(inner1Field.toStringWithSimpleNames()).isEqualTo("@A @C Inner1 inner1"); assertThat(shortNames(classInfo.getFieldInfo("inner2"))) - .isEqualTo("Outer.@A MiddleStatic.@B Inner2 inner2"); + .isEqualTo("Outer$@A MiddleStatic$@B Inner2 inner2"); assertThat(shortNames(classInfo.getFieldInfo("inner3"))) - .isEqualTo("Outer.MiddleStatic.@A InnerStatic inner3"); + .isEqualTo("Outer$MiddleStatic$@A InnerStatic inner3"); assertThat(shortNames(classInfo.getFieldInfo("inner4"))) - .isEqualTo("Outer.MiddleGeneric<@A Foo.@B Bar>.InnerGeneric<@D String @C []> inner4"); + .isEqualTo("Outer$MiddleGeneric<@A Foo$@B Bar>$InnerGeneric<@D String @C []> inner4"); final FieldInfo xyzField = classInfo.getFieldInfo("xyz"); - assertThat(shortNames(xyzField)).isEqualTo("List<@A X.@B Y.@C Z> xyz"); + assertThat(shortNames(xyzField)).isEqualTo("List<@A X$@B Y$@C Z> xyz"); assertThat(xyzField.toStringWithSimpleNames()).isEqualTo("List<@C Z> xyz"); - assertThat(shortNames(classInfo.getFieldInfo("xyz2"))).isEqualTo("List<@A X2.@B Y2.@C Z2> xyz2"); + assertThat(shortNames(classInfo.getFieldInfo("xyz2"))).isEqualTo("List<@A X2$@B Y2$@C Z2> xyz2"); - assertThat(shortNames(classInfo.getFieldInfo("xyz3"))).isEqualTo("List xyz3"); + assertThat(shortNames(classInfo.getFieldInfo("xyz3"))).isEqualTo("List xyz3"); - assertThat(shortNames(classInfo.getFieldInfo("xyz4"))).isEqualTo("List xyz4"); + assertThat(shortNames(classInfo.getFieldInfo("xyz4"))).isEqualTo("List xyz4"); assertThat(shortNames(classInfo.getMethodInfo("t").get(0))) .isEqualTo("<@A T extends @B U> @D U t(final @E T t)"); diff --git a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java index 82cb4340f..e7eecc145 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -55,6 +55,9 @@ public class MethodInfoTest { * The Class X. */ public static class X extends Exception { + /***/ + private static final long serialVersionUID = 1L; + /** * Method. */ @@ -137,11 +140,11 @@ public boolean accept(final MethodInfo methodInfo) { "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs(final java.lang.String str, " + "final char c, final long j, final float[] f, final byte[][] b, " - + "final java.util.List l, " - + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " - + "final java.lang.String[]... varargs)", - "public void throwsException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X", - "public void throwsGenericException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X, X2", + + "final java.util.List l, " + "final " + X.class.getName() + + "[][][] xArray, " + "final java.lang.String[]... varargs)", + "public void throwsException() throws " + X.class.getName(), + "public void throwsGenericException() throws " + + X.class.getName() + ", X2", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", "@" + Test.class.getName() + " public void testGetConstructorInfo()", @@ -182,12 +185,12 @@ public boolean accept(final MethodInfo methodInfo) { "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs(final java.lang.String str, " + "final char c, final long j, final float[] f, final byte[][] b, " - + "final java.util.List l, " - + "final io.github.classgraph.test.methodinfo.MethodInfoTest.X[][][] xArray, " - + "final java.lang.String[]... varargs)", + + "final java.util.List l, " + "final " + X.class.getName() + + "[][][] xArray, " + "final java.lang.String[]... varargs)", "private static java.lang.String[] privateMethod()", - "public void throwsException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X", - "public void throwsGenericException() throws io.github.classgraph.test.methodinfo.MethodInfoTest.X, X2", + "public void throwsException() throws " + X.class.getName(), + "public void throwsGenericException() throws " + + X.class.getName() + ", X2", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", "@" + Test.class.getName() + " public void testGetConstructorInfo()", @@ -221,7 +224,7 @@ public void testMethodInfoLoadMethodForArrayArg() { } } assertThat(arrayClassInfoList.toString()).isEqualTo("[class float[], class byte[][], " + "class " - + X.class.getName().replace('$', '.') + "[][][], " + "class java.lang.String[][]]"); + + X.class.getName() + "[][][], " + "class java.lang.String[][]]"); final ArrayClassInfo p1 = arrayClassInfoList.get(1); assertThat(p1.loadElementClass()).isEqualTo(byte.class); assertThat(p1.loadClass()).isEqualTo(byte[][].class); From 98c8aa335efb5bafd2367cd1461afffe1632d437 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 21:09:51 -0600 Subject: [PATCH 368/713] Don't sort interfaces alphabetically; add class type descriptor (#662) --- .../java/io/github/classgraph/ClassInfo.java | 47 ++++++++++- .../github/classgraph/ClassTypeSignature.java | 50 ++++++++++-- .../features/ClassTypeAnnotation.java | 80 ++++++++++++++++--- 3 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 5c2a6dea5..5fd1809b5 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -92,6 +92,9 @@ public class ClassInfo extends ScanResultObject implements Comparable /** The class type signature, parsed. */ private transient ClassTypeSignature typeSignature; + /** The synthetic class type descriptor. */ + private transient ClassTypeSignature typeDescriptor; + /** The fully-qualified defining method name, for anonymous inner classes. */ private String fullyQualifiedDefiningMethodName; @@ -1803,8 +1806,9 @@ public ClassInfoList getInterfaces() { .filterClassInfo(RelType.IMPLEMENTED_INTERFACES, /* strictAccept = */ false).reachableClasses; allInterfaces.addAll(superclassImplementedInterfaces); } + // Can't sort interfaces by name, since their order is significant in the definition of inheritance return new ClassInfoList(allInterfaces, implementedInterfaces.directlyRelatedClasses, - /* sortByName = */ true); + /* sortByName = */ false); } /** @@ -2865,6 +2869,47 @@ public String getTypeSignatureStr() { return typeSignatureStr; } + /** + * Returns the parsed type signature for this class, possibly including type parameters. If the type signature + * is not present for this class, indicating that this is not a generic class, then a type descriptor will be + * synthesized and returned, as if there were a type descriptor (classfiles may have a type signature but do not + * contain a type descriptor). May include type annotations on the superclass or interface(s). + * + * @return The parsed generic type signature for the class, or if not available, the synthetic type descriptor + * for the class. + */ + public ClassTypeSignature getTypeSignatureOrTypeDescriptor() { + ClassTypeSignature typeSig = null; + try { + typeSig = getTypeSignature(); + if (typeSig != null) { + return typeSig; + } + } catch (final Exception e) { + // Ignore + } + return getTypeDescriptor(); + } + + /** + * Returns a synthetic type descriptor for the method, created from the class name, superclass name, and + * implemented interfaces. May include type annotations on the superclass or interface(s). + * + * @return The synthetic type descriptor for the class. + */ + public ClassTypeSignature getTypeDescriptor() { + if (typeDescriptor == null) { + typeDescriptor = new ClassTypeSignature(this, getSuperclass(), getInterfaces()); + typeDescriptor.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final ClassTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + } + } + return typeDescriptor; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 4fc34045d..ce18ef880 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -33,6 +33,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import io.github.classgraph.Classfile.TypePathNode; @@ -91,6 +92,45 @@ private ClassTypeSignature(final ClassInfo classInfo, final List this.throwsSignatures = throwsSignatures; } + /** + * Constructor used to create synthetic class type descriptor (#662). + * + * @param classInfo + * The class. + * @param superclass + * The superclass. + * @param interfaces + * The implemented interfaces. + */ + ClassTypeSignature(final ClassInfo classInfo, final ClassInfo superclass, final ClassInfoList interfaces) { + super(); + this.classInfo = classInfo; + this.typeParameters = Collections.emptyList(); + ClassRefTypeSignature superclassSignature = null; + try { + superclassSignature = superclass == null ? null + : (ClassRefTypeSignature) TypeSignature + .parse("L" + superclass.getName().replace('.', '/') + ";", classInfo.getName()); + } catch (final ParseException e) { + // Silently fail (should not happen) + } + this.superclassSignature = superclassSignature; + this.superinterfaceSignatures = interfaces == null || interfaces.isEmpty() ? Collections.emptyList() + : new ArrayList(); + if (interfaces != null) { + for (final ClassInfo iface : interfaces) { + try { + final ClassRefTypeSignature ifaceSignature = (ClassRefTypeSignature) TypeSignature + .parse("L" + iface.getName().replace('.', '/') + ";", classInfo.getName()); + this.superinterfaceSignatures.add(ifaceSignature); + } catch (final ParseException e) { + // Silently fail (should not happen) + } + } + } + this.throwsSignatures = null; + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -227,8 +267,8 @@ protected void findReferencedClassInfo(final Map classNameToC */ @Override public int hashCode() { - return typeParameters.hashCode() + superclassSignature.hashCode() * 7 - + superinterfaceSignatures.hashCode() * 15; + return typeParameters.hashCode() + (superclassSignature == null ? 1 : superclassSignature.hashCode()) * 7 + + (superinterfaceSignatures == null ? 1 : superinterfaceSignatures.hashCode()) * 15; } /* (non-Javadoc) @@ -242,9 +282,9 @@ public boolean equals(final Object obj) { return false; } final ClassTypeSignature o = (ClassTypeSignature) obj; - return o.typeParameters.equals(this.typeParameters) - && o.superclassSignature.equals(this.superclassSignature) - && o.superinterfaceSignatures.equals(this.superinterfaceSignatures); + return Objects.equals(o.typeParameters, this.typeParameters) + && Objects.equals(o.superclassSignature, this.superclassSignature) + && Objects.equals(o.superinterfaceSignatures, this.superinterfaceSignatures); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java index 651ee42f5..6759be5fd 100644 --- a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; import io.github.classgraph.ScanResult; /** @@ -17,7 +16,17 @@ class ClassTypeAnnotation { /***/ @Retention(RetentionPolicy.RUNTIME) - private static @interface X { + private static @interface P { + } + + /***/ + @Retention(RetentionPolicy.RUNTIME) + private static @interface Q { + } + + /***/ + @Retention(RetentionPolicy.RUNTIME) + private static @interface R { } /***/ @@ -33,25 +42,70 @@ private static interface B { } /***/ - private static class Y extends @X Z implements A, B { + private static class E extends @P Z implements @Q A, @R B { + } + + /***/ + private static class F extends @P Z implements @Q A, @R B { + } + + /***/ + private static class G extends @P Z implements @Q B, @R A { + } + + /***/ + private static class H extends @P Z { + } + + /***/ + private static class I implements @Q B, @R A { } @Test void classTypeAnnotation() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(ClassTypeAnnotation.class.getPackage().getName()).enableAllInfo().scan()) { - final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); - assertThat(classInfo).isNotNull(); - // This is - // Y extends ClassTypeAnnotation.@X Z + // Type with annotations should be rendered by toString() as + // Y extends ClassTypeAnnotation$@X Z // and not - // Y extends @X ClassTypeAnnotation.Z - // Because the annotation is on Z, not ClassTypeAnnotation - assertThat(classInfo.getTypeSignature().toString()).isEqualTo("private static class " - + Y.class.getName() + " extends " + ClassTypeAnnotation.class.getName() + "$@" - + X.class.getName() + " " + Z.class.getSimpleName() + " implements " + A.class.getName() + ", " - + B.class.getName()); + // Y extends @X ClassTypeAnnotation$Z + // because the annotation is on Z, not ClassTypeAnnotation + + assertThat(scanResult.getClassInfo(E.class.getName()).getTypeSignature().toString()) + .isEqualTo("private static class " + E.class.getName() + " extends " + + ClassTypeAnnotation.class.getName() + "$@" + P.class.getName() + " " + + Z.class.getSimpleName() + " implements " + ClassTypeAnnotation.class.getName() + "$@" + + Q.class.getName() + " " + A.class.getSimpleName() + ", " + + ClassTypeAnnotation.class.getName() + "$@" + R.class.getName() + " " + + B.class.getSimpleName()); + + assertThat(scanResult.getClassInfo(F.class.getName()).getTypeSignatureOrTypeDescriptor().toString()) + .isEqualTo("private static class " + F.class.getName() + " extends " + + ClassTypeAnnotation.class.getName() + "$@" + P.class.getName() + " " + + Z.class.getSimpleName() + " implements " + ClassTypeAnnotation.class.getName() + "$@" + + Q.class.getName() + " " + A.class.getSimpleName() + ", " + + ClassTypeAnnotation.class.getName() + "$@" + R.class.getName() + " " + + B.class.getSimpleName()); + + assertThat(scanResult.getClassInfo(G.class.getName()).getTypeSignatureOrTypeDescriptor().toString()) + .isEqualTo("private static class " + G.class.getName() + " extends " + + ClassTypeAnnotation.class.getName() + "$@" + P.class.getName() + " " + + Z.class.getSimpleName() + " implements " + ClassTypeAnnotation.class.getName() + "$@" + + Q.class.getName() + " " + B.class.getSimpleName() + ", " + + ClassTypeAnnotation.class.getName() + "$@" + R.class.getName() + " " + + A.class.getSimpleName()); + + assertThat(scanResult.getClassInfo(H.class.getName()).getTypeSignatureOrTypeDescriptor().toString()) + .isEqualTo("private static class " + H.class.getName() + " extends " + + ClassTypeAnnotation.class.getName() + "$@" + P.class.getName() + " " + + Z.class.getSimpleName()); + + assertThat(scanResult.getClassInfo(I.class.getName()).getTypeSignatureOrTypeDescriptor().toString()) + .isEqualTo("private static class " + I.class.getName() + " implements " + + ClassTypeAnnotation.class.getName() + "$@" + Q.class.getName() + " " + + B.class.getSimpleName() + ", " + ClassTypeAnnotation.class.getName() + "$@" + + R.class.getName() + " " + A.class.getSimpleName()); } } } From 0ff806e708fc4771ba64dca5fa8cfb5e14797999 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 21:36:09 -0600 Subject: [PATCH 369/713] Fix compilation error --- pom.xml | 2 +- .../io/github/classgraph/ClassTypeSignature.java | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 804843389..2b953030e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.140 + classgraph-4.8.142 diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index ce18ef880..1bfdd8f0c 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -115,8 +115,9 @@ private ClassTypeSignature(final ClassInfo classInfo, final List // Silently fail (should not happen) } this.superclassSignature = superclassSignature; - this.superinterfaceSignatures = interfaces == null || interfaces.isEmpty() ? Collections.emptyList() - : new ArrayList(); + this.superinterfaceSignatures = interfaces == null || interfaces.isEmpty() + ? Collections. emptyList() + : new ArrayList(interfaces.size()); if (interfaces != null) { for (final ClassInfo iface : interfaces) { try { @@ -230,8 +231,10 @@ protected void findReferencedClassNames(final Set refdClassNames) { if (superclassSignature != null) { superclassSignature.findReferencedClassNames(refdClassNames); } - for (final ClassRefTypeSignature typeSignature : superinterfaceSignatures) { - typeSignature.findReferencedClassNames(refdClassNames); + if (superinterfaceSignatures != null) { + for (final ClassRefTypeSignature typeSignature : superinterfaceSignatures) { + typeSignature.findReferencedClassNames(refdClassNames); + } } if (throwsSignatures != null) { for (final ClassRefOrTypeVariableSignature typeSignature : throwsSignatures) { @@ -352,7 +355,7 @@ void toStringInternal(final String className, final boolean useSimpleNames, fina buf.append(superSig); } } - if (!superinterfaceSignatures.isEmpty()) { + if (superinterfaceSignatures != null && !superinterfaceSignatures.isEmpty()) { buf.append(isInterface ? " extends " : " implements "); for (int i = 0; i < superinterfaceSignatures.size(); i++) { if (i > 0) { From a7302aef26c94fc0b592245140520c1deb3ca28d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 21:38:39 -0600 Subject: [PATCH 370/713] Fix compilation error --- .../io/github/classgraph/features/ClassTypeAnnotation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java index 6759be5fd..b33815669 100644 --- a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.junit.jupiter.api.Test; @@ -16,16 +18,19 @@ class ClassTypeAnnotation { /***/ @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE_USE) private static @interface P { } /***/ @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE_USE) private static @interface Q { } /***/ @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE_USE) private static @interface R { } From b0e57876c48444a760df62631638b9864e1c95c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 21:38:58 -0600 Subject: [PATCH 371/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b953030e..7c2917617 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.142-SNAPSHOT + 4.8.143-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From c7af04ff69a067e9ad4091c2ee68f5d18d7a3b64 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 25 Mar 2022 21:43:12 -0600 Subject: [PATCH 372/713] Try fixing GitHub Actions compilation issues --- .../io/github/classgraph/features/ClassTypeAnnotation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java index b33815669..dd2d91aad 100644 --- a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -18,19 +18,19 @@ class ClassTypeAnnotation { /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE_USE) + @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) private static @interface P { } /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE_USE) + @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) private static @interface Q { } /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE_USE) + @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) private static @interface R { } From 3abd91198524a6660367d22b03f0cfed4db19dbb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Mar 2022 00:57:41 -0600 Subject: [PATCH 373/713] toString() fix --- src/main/java/io/github/classgraph/MethodInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 0eb8fe03c..e6e442100 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -1075,7 +1075,9 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { buf); } - buf.append(' '); + if (buf.length() > 0) { + buf.append(' '); + } if (name != null) { buf.append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); } From d43357de6ce2c4ca6af3e75c761c1d4b10841c0d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Mar 2022 02:43:20 -0600 Subject: [PATCH 374/713] Make method parameter information alignment more robust (#660) --- .../java/io/github/classgraph/MethodInfo.java | 222 +++++++++++------- .../features/ClassTypeAnnotation.java | 6 +- 2 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index e6e442100..8bb6f9e29 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -221,8 +221,47 @@ public MethodTypeSignature getTypeDescriptor() { typeDescriptor = MethodTypeSignature.parse(typeDescriptorStr, declaringClassName); typeDescriptor.setScanResult(scanResult); if (typeAnnotationDecorators != null) { - for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeDescriptor); + // It is possible that there are extra implicit params added at the beginning of the + // parameter list that type annotations don't take into account. Assume that the + // type signature has the correct number of parameters, and temporarily remove any + // implicit prefix parameters during the type decoration process. See getParameterInfo(). + int sigNumParam = 0; + final MethodTypeSignature sig = getTypeSignature(); + if (sig == null) { + // There is no type signature -- run type annotation decorators on descriptor + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + } else { + // Determine how many extra implicit params there are + sigNumParam = sig.getParameterTypeSignatures().size(); + final int descNumParam = typeDescriptor.getParameterTypeSignatures().size(); + final int numImplicitPrefixParams = descNumParam - sigNumParam; + if (numImplicitPrefixParams < 0) { + // Sanity check -- should not happen + throw new IllegalArgumentException( + "Fewer params in method type descriptor than in method type signature"); + } else if (numImplicitPrefixParams == 0) { + // There are no implicit prefix params -- run type annotation decorators on descriptor + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + } else { + // There are implicit prefix params -- strip them temporarily from type descriptor, + // then run decorators, then add them back again + final List paramSigs = typeDescriptor.getParameterTypeSignatures(); + final List strippedParamSigs = paramSigs.subList(0, + numImplicitPrefixParams); + for (int i = 0; i < numImplicitPrefixParams; i++) { + paramSigs.remove(0); + } + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + for (int i = numImplicitPrefixParams - 1; i >= 0; --i) { + paramSigs.add(0, strippedParamSigs.get(i)); + } + } } } } catch (final ParseException e) { @@ -501,64 +540,68 @@ public boolean isDefault() { * @return The {@link MethodParameterInfo} objects for the method parameters, one per parameter. */ public MethodParameterInfo[] getParameterInfo() { + // Kotlin is very inconsistent about the arity of each of the parameter metadata types, see: + // https://github.com/classgraph/classgraph/issues/175#issuecomment-363031510 + // As a workaround, we assume that any synthetic / mandated parameters must come first in the + // parameter list, when the arities don't match, and we right-align the metadata fields. + // This is probably the safest assumption across JVM languages, even though this convention + // is by no means the only possibility. (Unfortunately we can't just rely on the modifier + // bits to find synthetic / mandated parameters, because these bits are not always available, + // and even when they are, they don't always give the right alignment, at least for Kotlin- + // generated code). + + // Actually the Java spec says specifically: "The signature and descriptor of a given method + // or constructor may not correspond exactly, due to compiler-generated artifacts. In particular, + // the number of TypeSignatures that encode formal arguments in MethodTypeSignature may be less + // than the number of ParameterDescriptors in MethodDescriptor." + + // This was also triggered by an implicit param in Guava 28.2 (#660). + if (parameterInfo == null) { - // Get params from the type descriptor, and from the type signature if available - List paramTypeDescriptors = null; - int numParams = 0; - try { - final MethodTypeSignature typeSig = getTypeDescriptor(); - if (typeSig != null) { - paramTypeDescriptors = typeSig.getParameterTypeSignatures(); - numParams = paramTypeDescriptors.size(); - } - } catch (final Exception e) { - // Ignore - } + // Get param type signatures from the type signature of the method List paramTypeSignatures = null; + final MethodTypeSignature typeSig = getTypeSignature(); + if (typeSig != null) { + paramTypeSignatures = typeSig.getParameterTypeSignatures(); + } + + // If there is no type signature (i.e. if this is not a generic method), fall back to the type + // descriptor (N.B. the type descriptor is basically junk, because the compiler may prepend + // `synthetic` and/or `bridge` parameters automatically, without providing any modifiers for + // the method, so that it is impossible to know how many parameters have been prepended -- + // see #660.) + List paramTypeDescriptors = null; try { - final MethodTypeSignature typeSig = getTypeSignature(); - if (typeSig != null) { - paramTypeSignatures = typeSig.getParameterTypeSignatures(); + final MethodTypeSignature typeDesc = getTypeDescriptor(); + if (typeDesc != null) { + paramTypeDescriptors = typeDesc.getParameterTypeSignatures(); } } catch (final Exception e) { - // Ignore + // Ignore any IllegalArgumentExceptions triggered when type annotations are not able to be + /// aligned with parameters, when there is a `synthetic`, `bridge` or `mandated` parameter + // added to the first parameter position. } - // Figure out the number of params in the alignment (should be num params in type descriptor) - if (paramTypeSignatures != null && paramTypeSignatures.size() > numParams) { - // Should not happen - throw new ClassGraphException( - "typeSignatureParamTypes.size() > typeDescriptorParamTypes.size() for method " - + declaringClassName + "." + name); + // Find the max length of all the parameter information sources + int numParams = paramTypeSignatures == null ? 0 : paramTypeSignatures.size(); + if (paramTypeDescriptors != null && paramTypeDescriptors.size() > numParams) { + numParams = paramTypeDescriptors.size(); } - - // Figure out number of other fields that need alignment, and check length for consistency - final int otherParamMax = Math.max(parameterNames == null ? 0 : parameterNames.length, - Math.max(parameterModifiers == null ? 0 : parameterModifiers.length, - parameterAnnotationInfo == null ? 0 : parameterAnnotationInfo.length)); - if (otherParamMax > numParams) { - // Should not happen - throw new ClassGraphException("Type descriptor for method " + declaringClassName + "." + name - + " has insufficient parameters"); + if (parameterNames != null && parameterNames.length > numParams) { + numParams = parameterNames.length; + } + if (parameterModifiers != null && parameterModifiers.length > numParams) { + numParams = parameterModifiers.length; + } + if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > numParams) { + numParams = parameterAnnotationInfo.length; } - // Kotlin is very inconsistent about the arity of each of the parameter metadata types, see: - // https://github.com/classgraph/classgraph/issues/175#issuecomment-363031510 - // As a workaround, we assume that any synthetic / mandated parameters must come first in the - // parameter list, when the arities don't match, and we right-align the metadata fields. - // This is probably the safest assumption across JVM languages, even though this convention - // is by no means the only possibility. (Unfortunately we can't just rely on the modifier - // bits to find synthetic / mandated parameters, because these bits are not always available, - // and even when they are, they don't always give the right alignment, at least for Kotlin- - // generated code). - - // Actually the Java spec says specifically: "The signature and descriptor of a given method - // or constructor may not correspond exactly, due to compiler-generated artifacts. In particular, - // the number of TypeSignatures that encode formal arguments in MethodTypeSignature may be less - // than the number of ParameterDescriptors in MethodDescriptor." + // "Right-align" all parameter info, i.e. assume that any automatically-added implicit parameters + // were added at the beginning of the parameter list, not the end. String[] paramNamesAligned = null; - if (parameterNames != null && numParams > 0) { + if (parameterNames != null && parameterNames.length > 0) { if (parameterNames.length == numParams) { // No alignment necessary paramNamesAligned = parameterNames; @@ -571,7 +614,7 @@ public MethodParameterInfo[] getParameterInfo() { } } int[] paramModifiersAligned = null; - if (parameterModifiers != null && numParams > 0) { + if (parameterModifiers != null && parameterModifiers.length > 0) { if (parameterModifiers.length == numParams) { // No alignment necessary paramModifiersAligned = parameterModifiers; @@ -585,7 +628,7 @@ public MethodParameterInfo[] getParameterInfo() { } } AnnotationInfo[][] paramAnnotationInfoAligned = null; - if (parameterAnnotationInfo != null && numParams > 0) { + if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > 0) { if (parameterAnnotationInfo.length == numParams) { // No alignment necessary paramAnnotationInfoAligned = parameterAnnotationInfo; @@ -599,20 +642,35 @@ public MethodParameterInfo[] getParameterInfo() { } } List paramTypeSignaturesAligned = null; - if (paramTypeSignatures != null && numParams > 0) { + if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) { if (paramTypeSignatures.size() == numParams) { // No alignment necessary paramTypeSignaturesAligned = paramTypeSignatures; } else { // Right-align when not the right length paramTypeSignaturesAligned = new ArrayList<>(numParams); - for (int i = 0, n = numParams - paramTypeSignatures.size(); i < n; i++) { + for (int i = 0, lenDiff = numParams - paramTypeSignatures.size(); i < lenDiff; i++) { // Left-pad with nulls paramTypeSignaturesAligned.add(null); } paramTypeSignaturesAligned.addAll(paramTypeSignatures); } } + List paramTypeDescriptorsAligned = null; + if (paramTypeDescriptors != null && paramTypeDescriptors.size() > 0) { + if (paramTypeDescriptors.size() == numParams) { + // No alignment necessary + paramTypeDescriptorsAligned = paramTypeDescriptors; + } else { + // Right-align when not the right length + paramTypeDescriptorsAligned = new ArrayList<>(numParams); + for (int i = 0, lenDiff = numParams - paramTypeDescriptors.size(); i < lenDiff; i++) { + // Left-pad with nulls + paramTypeDescriptorsAligned.add(null); + } + paramTypeDescriptorsAligned.addAll(paramTypeDescriptors); + } + } // Generate MethodParameterInfo entries parameterInfo = new MethodParameterInfo[numParams]; @@ -620,7 +678,7 @@ public MethodParameterInfo[] getParameterInfo() { parameterInfo[i] = new MethodParameterInfo(this, paramAnnotationInfoAligned == null ? null : paramAnnotationInfoAligned[i], paramModifiersAligned == null ? 0 : paramModifiersAligned[i], - paramTypeDescriptors == null ? null : paramTypeDescriptors.get(i), + paramTypeDescriptorsAligned == null ? null : paramTypeDescriptorsAligned.get(i), paramTypeSignaturesAligned == null ? null : paramTypeSignaturesAligned.get(i), paramNamesAligned == null ? null : paramNamesAligned[i]); parameterInfo[i].setScanResult(scanResult); @@ -1127,39 +1185,45 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { MethodParameterInfo.modifiersToString(paramInfo.getModifiers(), buf); final TypeSignature paramTypeSignature = paramInfo.getTypeSignatureOrTypeDescriptor(); - if (i == varArgsParamIndex) { - // Show varargs params correctly -- replace last "[]" with "..." - if (!(paramTypeSignature instanceof ArrayTypeSignature)) { - throw new IllegalArgumentException( - "Got non-array type for last parameter of varargs method " + name); - } - final ArrayTypeSignature arrayType = (ArrayTypeSignature) paramTypeSignature; - if (arrayType.getNumDimensions() == 0) { - throw new IllegalArgumentException( - "Got a zero-dimension array type for last parameter of varargs method " + name); - } - arrayType.getElementTypeSignature().toString(useSimpleNames, buf); - for (int j = 0; j < arrayType.getNumDimensions() - 1; j++) { - buf.append("[]"); - } - buf.append("..."); - } else { - // Exclude parameter annotations from type annotations at toplevel of type signature, - // so that annotation is not listed twice - final AnnotationInfoList annotationsToExclude; - if (paramInfo.annotationInfo == null || paramInfo.annotationInfo.length == 0) { - annotationsToExclude = null; + // Param type signature may be null in the case of a `synthetic`, `bridge`, or `mandated` parameter + // implicitly added to a non-generic method + if (paramTypeSignature != null) { + if (i == varArgsParamIndex) { + // Show varargs params correctly -- replace last "[]" with "..." + if (!(paramTypeSignature instanceof ArrayTypeSignature)) { + throw new IllegalArgumentException( + "Got non-array type for last parameter of varargs method " + name); + } + final ArrayTypeSignature arrayType = (ArrayTypeSignature) paramTypeSignature; + if (arrayType.getNumDimensions() == 0) { + throw new IllegalArgumentException( + "Got a zero-dimension array type for last parameter of varargs method " + name); + } + arrayType.getElementTypeSignature().toString(useSimpleNames, buf); + for (int j = 0; j < arrayType.getNumDimensions() - 1; j++) { + buf.append("[]"); + } + buf.append("..."); } else { - annotationsToExclude = new AnnotationInfoList(paramInfo.annotationInfo.length); - annotationsToExclude.addAll(Arrays.asList(paramInfo.annotationInfo)); + // Exclude parameter annotations from type annotations at toplevel of type signature, + // so that annotation is not listed twice + final AnnotationInfoList annotationsToExclude; + if (paramInfo.annotationInfo == null || paramInfo.annotationInfo.length == 0) { + annotationsToExclude = null; + } else { + annotationsToExclude = new AnnotationInfoList(paramInfo.annotationInfo.length); + annotationsToExclude.addAll(Arrays.asList(paramInfo.annotationInfo)); + } + paramTypeSignature.toStringInternal(useSimpleNames, annotationsToExclude, buf); } - paramTypeSignature.toStringInternal(useSimpleNames, annotationsToExclude, buf); } if (hasParamNames) { final String paramName = paramInfo.getName(); if (paramName != null) { - buf.append(' '); + if (buf.charAt(buf.length() - 1) != ' ') { + buf.append(' '); + } buf.append(paramName); } } diff --git a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java index dd2d91aad..8e358e834 100644 --- a/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java +++ b/src/test/java/io/github/classgraph/features/ClassTypeAnnotation.java @@ -18,19 +18,19 @@ class ClassTypeAnnotation { /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) + @Target(value = { ElementType.TYPE_USE, ElementType.TYPE }) private static @interface P { } /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) + @Target(value = { ElementType.TYPE_USE, ElementType.TYPE }) private static @interface Q { } /***/ @Retention(RetentionPolicy.RUNTIME) - @Target(value = {ElementType.TYPE_USE, ElementType.TYPE}) + @Target(value = { ElementType.TYPE_USE, ElementType.TYPE }) private static @interface R { } From d32cfdf9c8ae41e1effcb0a39870d501dfc2dd7d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Mar 2022 02:43:49 -0600 Subject: [PATCH 375/713] [maven-release-plugin] prepare release classgraph-4.8.143 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7c2917617..0cd34877b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.143-SNAPSHOT + 4.8.143 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.142 + classgraph-4.8.143 From e0ca965dae5ac74299be7c3d7b26f003f7163575 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 26 Mar 2022 02:43:52 -0600 Subject: [PATCH 376/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0cd34877b..6c3a2db11 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.143 + 4.8.144-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.143 + classgraph-4.8.142 From dcec1de148bb0a04c50e35165c4375f7d52dab9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 02:01:30 +0000 Subject: [PATCH 377/713] Bump jmh-generator-annprocess from 1.34 to 1.35 Bumps jmh-generator-annprocess from 1.34 to 1.35. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c3a2db11..933d61593 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.34 + 1.35 test From 70caae025793a88a73cd7f209afe3a5b519f6e23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 02:01:32 +0000 Subject: [PATCH 378/713] Bump jmh-core from 1.34 to 1.35 Bumps jmh-core from 1.34 to 1.35. --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c3a2db11..d725cd094 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ org.openjdk.jmh jmh-core - 1.34 + 1.35 test From 8e3d3181b7bc24bd7ca5b34467d68fa83d72ee62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 02:01:39 +0000 Subject: [PATCH 379/713] Bump maven-surefire-plugin from 3.0.0-M5 to 3.0.0-M6 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M5 to 3.0.0-M6. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M5...surefire-3.0.0-M6) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c3a2db11..80bbb602d 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.0.0-M6 org.codehaus.mojo From 793317d3072feb50dff5af3e950043bf54192061 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 02:01:37 +0000 Subject: [PATCH 380/713] Bump maven-clean-plugin from 3.1.0 to 3.2.0 Bumps [maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.1.0...maven-clean-plugin-3.2.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c3a2db11..39d90e974 100644 --- a/pom.xml +++ b/pom.xml @@ -226,7 +226,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins From 1229ffc7ae69f2b95c812993506522a0e7cd54e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 03:01:37 +0000 Subject: [PATCH 381/713] Bump actions/setup-java from 2 to 3 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90d58c6d6..39408b08a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: ${{ matrix.java }} From f8c0eb84c3424655a6550f115d5328b7196b9a29 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 12 Apr 2022 22:44:06 -0600 Subject: [PATCH 382/713] Try suppressing ASOC static analysis false positive (#675) --- .../classgraph/fastzipfilereader/NestedJarHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index d836e5230..8ecc24c2d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -567,16 +567,16 @@ public void close() { throw new IOException("Got response code " + httpConn.getResponseCode() + " for URL " + url); } } - } else if (conn.getURL().getProtocol().equalsIgnoreCase("file")) { + } else if (url.getProtocol().equalsIgnoreCase("file")) { // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that // rewrites its URLs into "file:" URLs (see Issue400.java). try { // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile // (this avoids going through an InputStream). Throws IOException if the file cannot be read. - final File file = new File(conn.getURL().toURI()); + final File file = Paths.get(url.toURI()).toFile(); return new PhysicalZipFile(file, this, log); - } catch (final URISyntaxException e) { + } catch (final Exception e) { // Fall through to open URL as InputStream below } } From b477b12661b8d6e9a11a78a5e8e415b68eb0120c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 02:41:52 -0600 Subject: [PATCH 383/713] Fix for classpath ordering issue (#673) --- .classpath | 2 +- .../github/classgraph/ClasspathElement.java | 36 +- .../classgraph/ClasspathElementFileDir.java | 21 +- .../classgraph/ClasspathElementModule.java | 6 +- .../classgraph/ClasspathElementPathDir.java | 14 +- .../classgraph/ClasspathElementZip.java | 17 +- .../java/io/github/classgraph/Scanner.java | 331 +++++++++++------- .../classgraph/classpath/ClasspathOrder.java | 38 +- .../classgraph/concurrency/SingletonMap.java | 67 +++- .../classgraph/utils/CollectionUtils.java | 17 + .../issues/issue673/Issue673Test.java | 31 ++ src/test/resources/issue673/a.zip | Bin 0 -> 341 bytes src/test/resources/issue673/b.zip | Bin 0 -> 341 bytes src/test/resources/issue673/c.zip | Bin 0 -> 154 bytes 14 files changed, 386 insertions(+), 194 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java create mode 100644 src/test/resources/issue673/a.zip create mode 100644 src/test/resources/issue673/b.zip create mode 100644 src/test/resources/issue673/c.zip diff --git a/.classpath b/.classpath index 5576b4361..1fb85a3c1 100644 --- a/.classpath +++ b/.classpath @@ -31,7 +31,7 @@ - + diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 6afc519ca..a4c6c6e18 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -31,14 +31,15 @@ import java.io.File; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -49,7 +50,7 @@ import nonapi.io.github.classgraph.utils.LogNode; /** A classpath element (a directory or jarfile on the classpath). */ -abstract class ClasspathElement { +abstract class ClasspathElement implements Comparable { /** The index of the classpath element within the classpath or module path. */ int classpathElementIdx; @@ -72,16 +73,18 @@ abstract class ClasspathElement { boolean containsSpecificallyAcceptedClasspathElementResourcePath; /** - * The child classpath elements, keyed by the order of the child classpath element within the Class-Path entry - * of the manifest file the child classpath element was listed in (or the position of the file within the sorted - * entries of a lib directory). + * The index of the classpath element within the parent classpath element (e.g. for classpath elements added via + * a Class-Path entry in the manifest). Set to -1 initially in case the same ClasspathElement is present twice + * in the classpath, as a child of different parent ClasspathElements. */ - final Queue> childClasspathElementsIndexed = new ConcurrentLinkedQueue<>(); + final AtomicInteger classpathElementIdxWithinParent = new AtomicInteger(-1); /** - * The child classpath elements, ordered by order within the parent classpath element. + * The child classpath elements, keyed by the order of the child classpath element within the Class-Path entry + * of the manifest file the child classpath element was listed in (or the position of the file within the sorted + * entries of a lib directory). */ - List childClasspathElementsOrdered; + Collection childClasspathElements = new ConcurrentLinkedQueue<>(); /** * Resources found within this classpath element that were accepted and not rejected. (Only written by one @@ -102,7 +105,7 @@ abstract class ClasspathElement { protected final AtomicBoolean scanned = new AtomicBoolean(false); /** The classloader that this classpath element was obtained from. */ - protected ClassLoader classLoader; + protected AtomicReference classLoader = new AtomicReference<>(); /** * The name of the module from the {@code module-info.class} module descriptor, if one is present in the root of @@ -123,20 +126,27 @@ abstract class ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElement(final ClassLoader classLoader, final ScanSpec scanSpec) { - this.classLoader = classLoader; + ClasspathElement(final ScanSpec scanSpec) { this.scanSpec = scanSpec; } // ------------------------------------------------------------------------------------------------------------- + // Sort in increasing order of classpathElementIdxWithinParent + @Override + public int compareTo(final ClasspathElement other) { + return this.classpathElementIdxWithinParent.get() - other.classpathElementIdxWithinParent.get(); + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Get the ClassLoader the classpath element was obtained from. * * @return the classloader */ ClassLoader getClassLoader() { - return classLoader; + return classLoader.get(); } /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 9b94e5fc9..703c21022 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -46,7 +46,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -78,16 +78,14 @@ class ClasspathElementFileDir extends ClasspathElement { * * @param classpathEltDir * the classpath element directory - * @param classLoader - * the classloader * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ ClasspathElementFileDir(final File classpathEltDir, final String packageRootPrefix, - final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(classLoader, scanSpec); + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + super(scanSpec); this.classpathEltDir = classpathEltDir; this.packageRootDir = new File(classpathEltDir, packageRootPrefix); this.nestedJarHandler = nestedJarHandler; @@ -124,7 +122,7 @@ void open(final WorkQueue workQueue, final LogNode log) log(classpathElementIdx, "Found lib jar: " + file, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(file.getPath(), classLoader), + new ClasspathElementAndPackageRoot(file.getPath(), getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } @@ -140,12 +138,11 @@ void open(final WorkQueue workQueue, final LogNode log) if (log != null) { log(classpathElementIdx, "Found package root: " + packageRoot, log); } - workQueue - .addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(classpathEltDir, packageRootPrefix, - classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + new ClasspathElementAndPackageRoot(classpathEltDir, packageRootPrefix, + getClassLoader()), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 23e8e9f4a..209469fa6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -81,17 +81,15 @@ class ClasspathElementModule extends ClasspathElement { * * @param moduleRef * the module ref - * @param classLoader - * the classloader * @param moduleRefToModuleReaderProxyRecyclerMap * the module ref to module reader proxy recycler map * @param scanSpec * the scan spec */ - ClasspathElementModule(final ModuleRef moduleRef, final ClassLoader classLoader, + ClasspathElementModule(final ModuleRef moduleRef, final SingletonMap, IOException> // moduleRefToModuleReaderProxyRecyclerMap, final ScanSpec scanSpec) { - super(classLoader, scanSpec); + super(scanSpec); this.moduleRefToModuleReaderProxyRecyclerMap = moduleRefToModuleReaderProxyRecyclerMap; this.moduleRef = moduleRef; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 5bc85ebc6..604263dc5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -50,7 +50,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -82,16 +82,14 @@ class ClasspathElementPathDir extends ClasspathElement { * * @param classpathEltPath * the classpath element {@link Path} - * @param classLoader - * the classloader * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementPathDir(final Path classpathEltPath, final String packageRoot, final ClassLoader classLoader, + ClasspathElementPathDir(final Path classpathEltPath, final String packageRoot, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(classLoader, scanSpec); + super(scanSpec); this.classpathEltPath = classpathEltPath; this.packageRootPath = classpathEltPath.resolve(packageRoot); this.nestedJarHandler = nestedJarHandler; @@ -125,7 +123,7 @@ void open(final WorkQueue workQueue, final LogNode log) log(classpathElementIdx, "Found lib jar: " + filePath, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(filePath, classLoader), + new ClasspathElementAndPackageRoot(filePath, getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } @@ -144,8 +142,8 @@ void open(final WorkQueue workQueue, final LogNode log) log(classpathElementIdx, "Found package root: " + packageRootPrefix, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(classpathEltPath, packageRootPrefix, - classLoader), + new ClasspathElementAndPackageRoot(classpathEltPath, packageRootPrefix, + getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 2fd9e7bfa..da29b3dc7 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -49,7 +49,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -100,16 +100,13 @@ class ClasspathElementZip extends ClasspathElement { * @param rawPathObj * the raw path to the jarfile as a {@link String}, possibly including "!"-delimited nested paths, or * a {@link URL}, {@link URI} ol {@link Path} for the jarfile. - * @param classLoader - * the classloader * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementZip(final Object rawPathObj, final ClassLoader classLoader, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(classLoader, scanSpec); + ClasspathElementZip(final Object rawPathObj, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + super(scanSpec); // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; @@ -221,7 +218,7 @@ void open(final WorkQueue workQueue, final LogNode log) subLog.log("Found nested lib jar: " + entryPath); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(entryPath, classLoader), + new ClasspathElementAndPackageRoot(entryPath, getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -262,8 +259,8 @@ void open(final WorkQueue workQueue, final LogNode log) // Schedule child classpath element for scanning workQueue.addWorkUnit( // new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(childClassPathEltPathWithPrefix, - classLoader), + new ClasspathElementAndPackageRoot(childClassPathEltPathWithPrefix, + getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -293,7 +290,7 @@ void open(final WorkQueue workQueue, final LogNode log) if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndClassLoader(childClassPathEltPath, classLoader), + new ClasspathElementAndPackageRoot(childClassPathEltPath, getClassLoader()), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 07536e41d..5aec6f146 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -36,6 +36,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystemNotFoundException; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; @@ -47,7 +48,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.concurrent.Callable; @@ -62,7 +62,7 @@ import io.github.classgraph.Classfile.ClassfileFormatException; import io.github.classgraph.Classfile.SkipClassException; import nonapi.io.github.classgraph.classpath.ClasspathFinder; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.classpath.ModuleFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; @@ -193,8 +193,9 @@ class Scanner implements Callable { || scanSpec.moduleAcceptReject.isSpecificallyAcceptedAndNotRejected(moduleName)) { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( - systemModuleRef, defaultClassLoader, - nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); + systemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + scanSpec); + classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -216,8 +217,9 @@ class Scanner implements Callable { if (scanSpec.moduleAcceptReject.isAcceptedAndNotRejected(moduleName)) { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( - nonSystemModuleRef, defaultClassLoader, - nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); + nonSystemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + scanSpec); + classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -251,70 +253,40 @@ class Scanner implements Callable { private static void findClasspathOrderRec(final ClasspathElement currClasspathElement, final Set visitedClasspathElts, final List order) { if (visitedClasspathElts.add(currClasspathElement)) { + // The classpath order requires a preorder traversal of the DAG of classpath dependencies if (!currClasspathElement.skipClasspathElement) { // Don't add a classpath element if it is marked to be skipped. order.add(currClasspathElement); + // Whether or not a classpath element should be skipped, add any child classpath elements that are + // not marked to be skipped (i.e. keep recursing below) } - // Whether or not a classpath element should be skipped, add any child classpath elements that are - // not marked to be skipped (i.e. keep recursing) - for (final ClasspathElement childClasspathElt : currClasspathElement.childClasspathElementsOrdered) { + // Sort child elements into correct order, then traverse to them in order + final List childClasspathElementsSorted = CollectionUtils + .sortCopy(currClasspathElement.childClasspathElements); + for (final ClasspathElement childClasspathElt : childClasspathElementsSorted) { findClasspathOrderRec(childClasspathElt, visitedClasspathElts, order); } } } - /** Comparator used to sort ClasspathElement values into increasing order of integer index key. */ - private static final Comparator> INDEXED_CLASSPATH_ELEMENT_COMPARATOR = // - new Comparator>() { - @Override - public int compare(final Entry o1, - final Entry o2) { - return o1.getKey() - o2.getKey(); - } - }; - - /** - * Sort a collection of indexed ClasspathElements into increasing order of integer index key. - * - * @param classpathEltsIndexed - * the indexed classpath elts - * @return the classpath elements, ordered by index - */ - private static List orderClasspathElements( - final Collection> classpathEltsIndexed) { - final List> classpathEltsIndexedOrdered = new ArrayList<>( - classpathEltsIndexed); - CollectionUtils.sortIfNotEmpty(classpathEltsIndexedOrdered, INDEXED_CLASSPATH_ELEMENT_COMPARATOR); - final List classpathEltsOrdered = new ArrayList<>(classpathEltsIndexedOrdered.size()); - for (final Entry ent : classpathEltsIndexedOrdered) { - classpathEltsOrdered.add(ent.getValue()); - } - return classpathEltsOrdered; - } - /** * Recursively perform a depth-first traversal of child classpath elements, breaking cycles if necessary, to * determine the final classpath element order. This causes child classpath elements to be inserted in-place in * the classpath order, after the parent classpath element that contained them. * - * @param uniqueClasspathElements - * the unique classpath elements - * @param toplevelClasspathEltsIndexed + * @param toplevelClasspathElts * the toplevel classpath elts, indexed by order within the toplevel classpath * @return the final classpath order, after depth-first traversal of child classpath elements */ - private List findClasspathOrder(final Set uniqueClasspathElements, - final Queue> toplevelClasspathEltsIndexed) { - final List toplevelClasspathEltsOrdered = orderClasspathElements( - toplevelClasspathEltsIndexed); - for (final ClasspathElement classpathElt : uniqueClasspathElements) { - classpathElt.childClasspathElementsOrdered = orderClasspathElements( - classpathElt.childClasspathElementsIndexed); - } + private List findClasspathOrder(final Set toplevelClasspathElts) { + // Sort toplevel classpath elements into their correct order + final List toplevelClasspathEltsSorted = CollectionUtils.sortCopy(toplevelClasspathElts); + + // Perform a depth-first preorder traversal of the DAG of classpath elements final Set visitedClasspathElts = new HashSet<>(); final List order = new ArrayList<>(); - for (final ClasspathElement toplevelClasspathElt : toplevelClasspathEltsOrdered) { - findClasspathOrderRec(toplevelClasspathElt, visitedClasspathElts, order); + for (final ClasspathElement elt : toplevelClasspathEltsSorted) { + findClasspathOrderRec(elt, visitedClasspathElts, order); } return order; } @@ -353,13 +325,13 @@ private void processWorkUnits(final Collection workUnits, final LogNode l /** Used to enqueue classpath elements for opening. */ static class ClasspathEntryWorkUnit { /** The raw classpath entry and associated {@link ClassLoader}. */ - private final ClasspathElementAndClassLoader rawClasspathEntry; + private final ClasspathElementAndPackageRoot rawClasspathEntry; /** The parent classpath element. */ private final ClasspathElement parentClasspathElement; /** The order within the parent classpath element. */ - private final int orderWithinParentClasspathElement; + private final int classpathElementIdxWithinParent; /** * Constructor. @@ -368,33 +340,65 @@ static class ClasspathEntryWorkUnit { * the raw classpath entry path and the classloader it was obtained from * @param parentClasspathElement * the parent classpath element - * @param orderWithinParentClasspathElement + * @param classpathElementIdxWithinParent * the order within parent classpath element */ - public ClasspathEntryWorkUnit(final ClasspathElementAndClassLoader rawClasspathEntry, - final ClasspathElement parentClasspathElement, final int orderWithinParentClasspathElement) { + public ClasspathEntryWorkUnit(final ClasspathElementAndPackageRoot rawClasspathEntry, + final ClasspathElement parentClasspathElement, final int classpathElementIdxWithinParent) { this.rawClasspathEntry = rawClasspathEntry; this.parentClasspathElement = parentClasspathElement; - this.orderWithinParentClasspathElement = orderWithinParentClasspathElement; + this.classpathElementIdxWithinParent = classpathElementIdxWithinParent; } } + /** + * A singleton map used to eliminate creation of duplicate {@link ClasspathElement} objects, to reduce the + * chance that resources are scanned twice, by mapping canonicalized Path objects, URLs, etc. to + * ClasspathElements. + */ + private final SingletonMap // + classpathEntryObjToClasspathEntrySingletonMap = // + new SingletonMap() { + @Override + public ClasspathElement newInstance(final Object classpathEntryObj, final LogNode log) + throws IOException, InterruptedException { + // Each use of this map provides a NewInstanceFactory + throw new IOException("This method should not be called"); + } + }; + /** * The classpath element singleton map. For each classpath element path, canonicalize path, and create a * ClasspathElement singleton. */ - private final SingletonMap // + private final SingletonMap // classpathEntryToClasspathElementSingletonMap = // - new SingletonMap() { + new SingletonMap() { @Override - public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpathEntry, + public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpathEntry, final LogNode log) throws IOException, InterruptedException { - Object classpathEntryObj = classpathEntry.classpathElementRoot; + Object classpathEntryObj = classpathEntry.classpathElementObj; + if (classpathEntryObj == null) { + // Should not happen + throw new IOException("Got null classpath entry object"); + } + String dirOrPathPackageRoot = classpathEntry.dirOrPathPackageRoot; while (dirOrPathPackageRoot.startsWith("/")) { dirOrPathPackageRoot = dirOrPathPackageRoot.substring(1); } + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end + if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { + final String classpathEntryStr = classpathEntryObj.toString(); + if (classpathEntryStr.endsWith("!/")) { + classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); + } else if (classpathEntryStr.endsWith("!")) { + classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); + } + } + // If classpath entry object is a URL-formatted string, convert to a URL instance if (classpathEntryObj instanceof String) { final String classpathEntryStr = (String) classpathEntryObj; @@ -402,7 +406,11 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa try { classpathEntryObj = new URL(classpathEntryStr); } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + classpathEntryStr); + try { + classpathEntryObj = new URI(classpathEntryStr); + } catch (final URISyntaxException e1) { + throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); + } } } } @@ -414,8 +422,22 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa final String scheme = classpathEntryURL.getProtocol(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + try { + return classpathEntryObjToClasspathEntrySingletonMap.get( + // Use toString() for the key so that URLs and URIs that resolve to the + // same resource will point to the same entry in the singleton map + classpathEntryURL.toString(), log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, + final LogNode log) { + return new ClasspathElementZip(classpathEntryURL, nestedJarHandler, + scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e) { + throw new IOException("Could not open URL: " + classpathEntryURL + " : " + e); + } } else { try { // See if the URL resolves to a file or directory via the Path API @@ -438,8 +460,22 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } } catch (final FileSystemNotFoundException e) { // This is a custom URL scheme without a backing FileSystem - return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + try { + return classpathEntryObjToClasspathEntrySingletonMap.get( + // Use toString() for the key so that URLs and URIs that resolve to the + // same resource will point to the same entry in the singleton map + classpathEntryURL.toString(), log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, + final LogNode log) { + return new ClasspathElementZip(classpathEntryURL, + nestedJarHandler, scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { + throw new IOException("Could not open URL: " + classpathEntryURL + " : " + e2); + } } } } else if (classpathEntryObj instanceof URI) { @@ -447,8 +483,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa final String scheme = classpathEntryURI.getScheme(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + return new ClasspathElementZip(classpathEntryURI, nestedJarHandler, scanSpec); } else { try { // See if the URI resolves to a file or directory via the Path API @@ -471,8 +506,22 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } } catch (final FileSystemNotFoundException e) { // This is a custom URI scheme without a backing FileSystem - return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + try { + return classpathEntryObjToClasspathEntrySingletonMap.get( + // Use toString() for the key so that URLs and URIs that resolve to the + // same resource will point to the same entry in the singleton map + classpathEntryURI.toString(), log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, + final LogNode log) { + return new ClasspathElementZip(classpathEntryURI, + nestedJarHandler, scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { + throw new IOException("Could not open URI: " + classpathEntryURI + " : " + e2); + } } } } else if (classpathEntryObj instanceof Path) { @@ -482,11 +531,29 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } if (classpathEntryPath != null) { + try { + // Canonicalize path, to avoid duplication + // Throws IOException if the file does not exist or an I/O error occurs + classpathEntryPath = classpathEntryPath.toRealPath(); + } catch (final SecurityException e) { + // Ignore + } final Path packageRootPath = classpathEntryPath.resolve(dirOrPathPackageRoot); if (FileUtils.canReadAndIsFile(packageRootPath)) { - // classpathEntryObj is a Path which points to a lib/ext jar inside a parent Path - return new ClasspathElementZip(classpathEntryPath, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + // packageRootPath is a Path which points to a lib/ext jar inside a parent Path + try { + return classpathEntryObjToClasspathEntrySingletonMap.get(packageRootPath, log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, + final LogNode log) { + return new ClasspathElementZip(packageRootPath, nestedJarHandler, + scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { + throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + } } else if (FileUtils.canReadAndIsDir(packageRootPath)) { if ("JrtFileSystem" .equals(packageRootPath.getFileSystem().getClass().getSimpleName())) { @@ -496,8 +563,21 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa + " (modules are scanned using the JPMS API)"); } // classpathEntryObj is a Path which points to a dir -- need to scan it recursively - return new ClasspathElementPathDir(classpathEntryPath, dirOrPathPackageRoot, - classpathEntry.classLoader, nestedJarHandler, scanSpec); + try { + final Path path = classpathEntryPath; + final String root = dirOrPathPackageRoot; + return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, + final LogNode log) { + return new ClasspathElementPathDir(path, root, nestedJarHandler, + scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { + throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + } } } @@ -533,32 +613,31 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } else { throw new IOException("Not a normal file or directory"); } - // Check if canonicalized path is the same as pre-canonicalized path - final String baseFileCanonicalPathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), - fileCanonicalized.getPath()); - final String canonicalPathNormalized = plingIdx < 0 ? baseFileCanonicalPathNormalized - : baseFileCanonicalPathNormalized + pathNormalized.substring(plingIdx); - if (!canonicalPathNormalized.equals(pathNormalized)) { - // If canonicalized path is not the same as pre-canonicalized path, need to recurse - // to map non-canonicalized path to singleton for canonicalized path (this should - // only recurse once, since File::getCanonicalFile and FastPathResolver::resolve are - // idempotent) - try { - return this.get(new ClasspathElementAndClassLoader(canonicalPathNormalized, - dirOrPathPackageRoot, classpathEntry.classLoader), log); - } catch (final NullSingletonException | NewInstanceException e) { - throw new IOException("Cannot get classpath element for canonical path " - + canonicalPathNormalized + " : " + (e.getCause() == null ? e : e.getCause())); - } - } else { - // Otherwise path is already canonical, and this is the first time this path has - // been seen -- instantiate a ClasspathElementZip or ClasspathElementDir singleton - // for the classpath element path - return isJar - ? new ClasspathElementZip(canonicalPathNormalized, classpathEntry.classLoader, - nestedJarHandler, scanSpec) - : new ClasspathElementFileDir(fileCanonicalized, dirOrPathPackageRoot, - classpathEntry.classLoader, nestedJarHandler, scanSpec); + // Convert File into Path to try to merge dups + Path pathCanonicalized; + try { + pathCanonicalized = fileCanonicalized.toPath(); + } catch (final InvalidPathException e) { + throw new IOException("Could not convert File to Path: " + fileCanonicalized + " : " + e); + } + try { + final boolean jar = isJar; + final String root = dirOrPathPackageRoot; + return classpathEntryObjToClasspathEntrySingletonMap.get(pathCanonicalized, log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance(final Object key, final LogNode log) { + // Instantiate a ClasspathElementZip or ClasspathElementDir singleton + // for the classpath element path + return jar + ? new ClasspathElementZip(pathCanonicalized, nestedJarHandler, + scanSpec) + : new ClasspathElementFileDir(fileCanonicalized, root, + nestedJarHandler, scanSpec); + } + }); + } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { + throw new IOException("Could not open path: " + pathCanonicalized + " : " + e2); } } }; @@ -568,15 +647,16 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa * {@link ClasspathElementFileDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * - * @param openedClasspathElementsSet - * the opened classpath elements set - * @param toplevelClasspathEltOrder + * @param allClasspathElts + * on exit, the set of all classpath elements + * @param toplevelClasspathElts + * on exit, the toplevel classpath elements + * @param ClasspathEltOrder * the toplevel classpath elt order * @return the work unit processor */ private WorkUnitProcessor newClasspathEntryWorkUnitProcessor( - final Set openedClasspathElementsSet, - final Queue> toplevelClasspathEltOrder) { + final Set allClasspathElts, final Set toplevelClasspathElts) { return new WorkUnitProcessor() { @Override public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, @@ -588,6 +668,11 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, try { classpathElt = classpathEntryToClasspathElementSingletonMap.get(workUnit.rawClasspathEntry, log); + // Set index within parent, if it hasn't already been set + classpathElt.classpathElementIdxWithinParent.compareAndSet(-1, + workUnit.classpathElementIdxWithinParent); + // Set classloader, if it hasn't already been set + classpathElt.classLoader.compareAndSet(null, workUnit.rawClasspathEntry.classLoader); } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for classpath entry " + workUnit.rawClasspathEntry + " : " + (e.getCause() == null ? e : e.getCause())); @@ -596,7 +681,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // Only run open() once per ClasspathElement (it is possible for there to be // multiple classpath elements with different non-canonical paths that map to // the same canonical path, i.e. to the same ClasspathElement) - if (openedClasspathElementsSet.add(classpathElt)) { + if (allClasspathElts.add(classpathElt)) { final LogNode subLog = log == null ? null : log.log("Opening classpath element " + classpathElt); @@ -607,24 +692,17 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // queue if they are found. classpathElt.open(workQueue, subLog); - // Create a new tuple consisting of the order of the new classpath element - // within its parent, and the new classpath element. - // N.B. even if skipClasspathElement is true, still possibly need to scan child - // classpath elements (so still need to connect parent to child here) - final SimpleEntry classpathEltEntry = // - new SimpleEntry<>(workUnit.orderWithinParentClasspathElement, classpathElt); if (workUnit.parentClasspathElement != null) { // Link classpath element to its parent, if it is not a toplevel element - workUnit.parentClasspathElement.childClasspathElementsIndexed.add(classpathEltEntry); + workUnit.parentClasspathElement.childClasspathElements.add(classpathElt); } else { - // Record toplevel elements - toplevelClasspathEltOrder.add(classpathEltEntry); + toplevelClasspathElts.add(classpathElt); } } } catch (final IOException | SecurityException e) { if (log != null) { log.log("Skipping invalid classpath element " - + workUnit.rawClasspathEntry.classpathElementRoot + + workUnit.rawClasspathEntry.classpathElementObj + (workUnit.rawClasspathEntry.dirOrPathPackageRoot.isEmpty() ? "" : "/" + workUnit.rawClasspathEntry.dirOrPathPackageRoot) + " : " + e); @@ -1050,27 +1128,30 @@ private ScanResult performScan(final List finalClasspathEltOrd private ScanResult openClasspathElementsThenScan() throws InterruptedException, ExecutionException { // Get order of elements in traditional classpath final List rawClasspathEntryWorkUnits = new ArrayList<>(); - for (final ClasspathElementAndClassLoader rawClasspathEntry : classpathFinder.getClasspathOrder() - .getOrder()) { + final List rawClasspathOrder = classpathFinder.getClasspathOrder() + .getOrder(); + for (final ClasspathElementAndPackageRoot rawClasspathEntry : rawClasspathOrder) { rawClasspathEntryWorkUnits .add(new ClasspathEntryWorkUnit(rawClasspathEntry, /* parentClasspathElement = */ null, - /* orderWithinParentClasspathElement = */ rawClasspathEntryWorkUnits.size())); + // classpathElementIdxWithinParent is the original classpath index, + // for toplevel classpath elements + /* classpathElementIdxWithinParent = */ rawClasspathEntryWorkUnits.size())); } // In parallel, create a ClasspathElement singleton for each classpath element, then call open() // on each ClasspathElement object, which in the case of jarfiles will cause LogicalZipFile instances // to be created for each (possibly nested) jarfile, then will read the manifest file and zip entries. - final Set openedClasspathEltsSet = Collections + final Set allClasspathElts = Collections + .newSetFromMap(new ConcurrentHashMap()); + final Set toplevelClasspathElts = Collections .newSetFromMap(new ConcurrentHashMap()); - final Queue> toplevelClasspathEltOrder = new ConcurrentLinkedQueue<>(); processWorkUnits(rawClasspathEntryWorkUnits, topLevelLog == null ? null : topLevelLog.log("Opening classpath elements"), - newClasspathEntryWorkUnitProcessor(openedClasspathEltsSet, toplevelClasspathEltOrder)); + newClasspathEntryWorkUnitProcessor(allClasspathElts, toplevelClasspathElts)); // Determine total ordering of classpath elements, inserting jars referenced in manifest Class-Path // entries in-place into the ordering, if they haven't been listed earlier in the classpath already. - final List classpathEltOrder = findClasspathOrder(openedClasspathEltsSet, - toplevelClasspathEltOrder); + final List classpathEltOrder = findClasspathOrder(toplevelClasspathElts); // Find classpath elements that are path prefixes of other classpath elements, and for // ClasspathElementZip, get module-related manifest entry values diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 7e028264f..e43fad158 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -62,7 +62,7 @@ public class ClasspathOrder { private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); /** The classpath order. Keys are instances of {@link String} or {@link URL}. */ - private final List order = new ArrayList<>(); + private final List order = new ArrayList<>(); /** Suffixes for automatic package roots, e.g. "!/BOOT-INF/classes". */ private static final List AUTOMATIC_PACKAGE_ROOT_SUFFIXES = new ArrayList<>(); @@ -76,11 +76,11 @@ public class ClasspathOrder { /** * A classpath element and the {@link ClassLoader} it was obtained from. */ - public static class ClasspathElementAndClassLoader { + public static class ClasspathElementAndPackageRoot { /** * The classpath element root (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ - public final Object classpathElementRoot; + public final Object classpathElementObj; /** The classpath element package root, prefix, e.g. "BOOT-INF/classes" or "". */ public final String dirOrPathPackageRoot; @@ -91,17 +91,18 @@ public static class ClasspathElementAndClassLoader { /** * Constructor for directory or {@link Path} classpath entries. * - * @param classpathElementRoot - * the classpath element root (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). + * @param classpathElementObj + * the classpath element object (a {@link String} path, {@link Path}, {@link URL} or + * {@link URI}). * @param dirOrPathPackageRoot * the classpath element package root prefix, e.g. "BOOT-INF/classes" or "". Only used for * directory or {@link Path} classpath entries. * @param classLoader * the classloader the classpath element was obtained from. */ - public ClasspathElementAndClassLoader(final Object classpathElementRoot, final String dirOrPathPackageRoot, + public ClasspathElementAndPackageRoot(final Object classpathElementObj, final String dirOrPathPackageRoot, final ClassLoader classLoader) { - this.classpathElementRoot = classpathElementRoot; + this.classpathElementObj = classpathElementObj; this.dirOrPathPackageRoot = dirOrPathPackageRoot; this.classLoader = classLoader; } @@ -114,33 +115,32 @@ public ClasspathElementAndClassLoader(final Object classpathElementRoot, final S * @param classLoader * the classloader the classpath element was obtained from. */ - public ClasspathElementAndClassLoader(final Object classpathElementRoot, final ClassLoader classLoader) { - this.classpathElementRoot = classpathElementRoot; + public ClasspathElementAndPackageRoot(final Object classpathElementRoot, final ClassLoader classLoader) { + this.classpathElementObj = classpathElementRoot; this.dirOrPathPackageRoot = ""; this.classLoader = classLoader; } @Override public int hashCode() { - return Objects.hash(classpathElementRoot, dirOrPathPackageRoot, classLoader); + return Objects.hash(classpathElementObj, dirOrPathPackageRoot, classLoader); } @Override public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ClasspathElementAndClassLoader)) { + } else if (!(obj instanceof ClasspathElementAndPackageRoot)) { return false; } - final ClasspathElementAndClassLoader other = (ClasspathElementAndClassLoader) obj; + final ClasspathElementAndPackageRoot other = (ClasspathElementAndPackageRoot) obj; return Objects.equals(this.dirOrPathPackageRoot, other.dirOrPathPackageRoot) - && Objects.equals(this.classpathElementRoot, other.classpathElementRoot) - && Objects.equals(this.classLoader, other.classLoader); + && Objects.equals(this.classpathElementObj, other.classpathElementObj); } @Override public String toString() { - return classpathElementRoot + " [" + classLoader + "]"; + return classpathElementObj + " [" + classLoader + "]"; } } @@ -159,7 +159,7 @@ public String toString() { * * @return the classpath order. */ - public List getOrder() { + public List getOrder() { return order; } @@ -208,7 +208,7 @@ private boolean filter(final URL classpathElementURL, final String classpathElem */ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classLoader) { if (classpathEntryUniqueResolvedPaths.add(pathEntry)) { - order.add(new ClasspathElementAndClassLoader(pathEntry, classLoader)); + order.add(new ClasspathElementAndPackageRoot(pathEntry, classLoader)); return true; } return false; @@ -260,7 +260,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle // Deduplicate classpath elements if (classpathEntryUniqueResolvedPaths.add(pathElementStrWithoutSuffix)) { // Record classpath element in classpath order - order.add(new ClasspathElementAndClassLoader(pathElementWithoutSuffix, classLoader)); + order.add(new ClasspathElementAndPackageRoot(pathElementWithoutSuffix, classLoader)); return true; } } else { @@ -274,7 +274,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle return false; } if (classpathEntryUniqueResolvedPaths.add(pathElementStrResolved)) { - order.add(new ClasspathElementAndClassLoader(pathElementStrResolved, classLoader)); + order.add(new ClasspathElementAndPackageRoot(pathElementStrResolved, classLoader)); return true; } } diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java index 90a869ed1..c7b5931fc 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -163,6 +163,28 @@ V get() throws InterruptedException { */ public abstract V newInstance(K key, LogNode log) throws E, InterruptedException; + /** + * Create a new instance. + * + * @param + * The key type. + * @param + * The instance type. + */ + @FunctionalInterface + public interface NewInstanceFactory { + /** + * Create a new instance. + * + * @param key + * The key. + * @param log + * The log. + * @return The new instance. + */ + public V newInstance(K key, LogNode log); + } + /** * Check if the given key is in the map, and if so, return the value of {@link #newInstance(Object, LogNode)} * for that key, or block on the result of {@link #newInstance(Object, LogNode)} if another thread is currently @@ -174,6 +196,10 @@ V get() throws InterruptedException { * * @param key * The key for the singleton. + * @param newInstanceFactory + * if non-null, a factory for creating new instances, otherwise if null, then + * {@link #newInstance(Object, LogNode)} is called instead (this allows new instance creation to be + * overridden on a per-instance basis). * @param log * The log. * @return The non-null singleton instance, if {@link #newInstance(Object, LogNode)} returned a non-null @@ -189,7 +215,7 @@ V get() throws InterruptedException { * @throws NewInstanceException * if {@link #newInstance(Object, LogNode)} threw an exception. */ - public V get(final K key, final LogNode log) + public V get(final K key, final LogNode log, final NewInstanceFactory newInstanceFactory) throws E, InterruptedException, NullSingletonException, NewInstanceException { final SingletonHolder singletonHolder = map.get(key); @SuppressWarnings("null") @@ -209,7 +235,13 @@ public V get(final K key, final LogNode log) } else { try { // Create a new instance - instance = newInstance(key, log); + if (newInstanceFactory != null) { + // Call NewInstanceFactory + instance = newInstanceFactory.newInstance(key, log); + } else { + // Call overridden newInstance method + instance = newInstance(key, log); + } } catch (final Throwable t) { // Initialize newSingletonHolder with the new instance. @@ -229,6 +261,37 @@ public V get(final K key, final LogNode log) } } + /** + * Check if the given key is in the map, and if so, return the value of {@link #newInstance(Object, LogNode)} + * for that key, or block on the result of {@link #newInstance(Object, LogNode)} if another thread is currently + * creating the new instance. + * + * If the given key is not currently in the map, store a placeholder in the map for this key, then run + * {@link #newInstance(Object, LogNode)} for the key, store the result in the placeholder (which unblocks any + * other threads waiting for the value), and then return the new instance. + * + * @param key + * The key for the singleton. + * @param log + * The log. + * @return The non-null singleton instance, if {@link #newInstance(Object, LogNode)} returned a non-null + * instance on this call or a previous call, otherwise throws {@link NullPointerException} if this call + * or a previous call to {@link #newInstance(Object, LogNode)} returned null. + * @throws E + * If {@link #newInstance(Object, LogNode)} threw an exception. + * @throws InterruptedException + * if the thread was interrupted while waiting for the singleton to be instantiated by another + * thread. + * @throws NullSingletonException + * if {@link #newInstance(Object, LogNode)} returned null. + * @throws NewInstanceException + * if {@link #newInstance(Object, LogNode)} threw an exception. + */ + public V get(final K key, final LogNode log) + throws E, InterruptedException, NullSingletonException, NewInstanceException { + return get(key, log, null); + } + /** * Get all valid singleton values in the map. * diff --git a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java index 6134df200..9fc1ce89f 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java @@ -28,6 +28,8 @@ */ package nonapi.io.github.classgraph.utils; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; @@ -75,4 +77,19 @@ public static void sortIfNotEmpty(final List list, final Comparator> List sortCopy(final Collection elts) { + final List sortedCopy = new ArrayList<>(elts); + if (sortedCopy.size() > 1) { + Collections.sort(sortedCopy); + } + return sortedCopy; + } } diff --git a/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java new file mode 100644 index 000000000..8fd8df13c --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java @@ -0,0 +1,31 @@ +package io.github.classgraph.issues.issue673; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URL; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +class Issue673Test { + @Test + void testResourcesCanBeRead() { + // a has Class-Path manifest entry that points to b, b points to c + final URL aURL = Issue673Test.class.getClassLoader().getResource("issue673/a.zip"); + assertThat(aURL != null); + final URL bURL = Issue673Test.class.getClassLoader().getResource("issue673/b.zip"); + assertThat(bURL != null); + + // This succeeded before issue 673 was fixed + try (ScanResult scanResult = new ClassGraph().overrideClasspath(bURL, aURL).verbose().scan()) { + assertThat(scanResult.getAllResources().getPaths()).contains("C"); + } + + // This failed before issue 673 was fixed + try (ScanResult scanResult = new ClassGraph().overrideClasspath(aURL, bURL).verbose().scan()) { + assertThat(scanResult.getAllResources().getPaths()).contains("C"); + } + } +} diff --git a/src/test/resources/issue673/a.zip b/src/test/resources/issue673/a.zip new file mode 100644 index 0000000000000000000000000000000000000000..8d5beffc9c1224a597b68262ddfb1ffe7edc9aa7 GIT binary patch literal 341 zcmWIWW@h1H00I7OJt0e5^E{Y!F1{J>*(j{<{BKL=j#SGZwb(>rI_Y9=Oh*v z>jos2WLPOA>s4hIZ~-k~WRhdXGu Date: Thu, 14 Apr 2022 02:42:22 -0600 Subject: [PATCH 384/713] Remove verbose logging --- .../io/github/classgraph/issues/issue673/Issue673Test.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java index 8fd8df13c..d2733cc80 100644 --- a/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java +++ b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java @@ -19,12 +19,12 @@ void testResourcesCanBeRead() { assertThat(bURL != null); // This succeeded before issue 673 was fixed - try (ScanResult scanResult = new ClassGraph().overrideClasspath(bURL, aURL).verbose().scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(bURL, aURL).scan()) { assertThat(scanResult.getAllResources().getPaths()).contains("C"); } // This failed before issue 673 was fixed - try (ScanResult scanResult = new ClassGraph().overrideClasspath(aURL, bURL).verbose().scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(aURL, bURL).scan()) { assertThat(scanResult.getAllResources().getPaths()).contains("C"); } } From 94a49d55119080ea8f9cb65bc131c129fe0a584c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 02:53:03 -0600 Subject: [PATCH 385/713] Fix GitHub Actions compile error --- src/main/java/io/github/classgraph/Scanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 5aec6f146..26b3b0002 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -614,7 +614,7 @@ public ClasspathElement newInstance(final Object key, throw new IOException("Not a normal file or directory"); } // Convert File into Path to try to merge dups - Path pathCanonicalized; + final Path pathCanonicalized; try { pathCanonicalized = fileCanonicalized.toPath(); } catch (final InvalidPathException e) { From 16c88814c32fe6b39c973d40219e2aa90eede074 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 02:55:24 -0600 Subject: [PATCH 386/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 895a67817..672d07bcf 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ClassGraph is an uber-fast parallelized classpath scanner and module scanner for [![GitHub issues](https://img.shields.io/github/issues/classgraph/classgraph.svg)](https://github.com/classgraph/classgraph/issues/) [![lgtm alerts](https://img.shields.io/lgtm/alerts/g/classgraph/classgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/classgraph/classgraph/alerts/) [![lgtm code quality](https://img.shields.io/lgtm/grade/java/g/classgraph/classgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/classgraph/classgraph/context:java) -[![Codacy Badge](https://img.shields.io/codacy/grade/31d639dd3874454c917f80ea2bff8155.svg?style=flat)](https://www.codacy.com/app/lukehutch/classgraph) +[![Codacy Badge](https://img.shields.io/codacy/grade/31d639dd3874454c917f80ea2bff8155.svg?style=flat)](https://www.codacy.com/app/classgraph/classgraph)
[![Dependencies: none](https://img.shields.io/badge/dependencies-none-blue.svg)](#) [![Dependents](https://badgen.net/github/dependents-repo/classgraph/classgraph)](https://github.com/classgraph/classgraph/network/dependents?package_id=UGFja2FnZS0xODcxNTE4NTM%3D) From 5bc68ecd985915a9f2ab61c16d008af559040f99 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 03:10:49 -0600 Subject: [PATCH 387/713] Fix static analysis warnings --- .../github/classgraph/fileslice/PathSlice.java | 16 +++++++--------- .../classgraph/reflection/ReflectionDriver.java | 6 ++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java index 321e23109..0098073d6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -245,17 +245,15 @@ public int hashCode() { @Override public void close() { if (!isClosed.getAndSet(true)) { - if (isTopLevelFileSlice) { + if (isTopLevelFileSlice && fileChannel != null) { // Only close the FileChannel in the toplevel file slice, so that it is only closed once - if (fileChannel != null) { - try { - // Closing raf will also close the associated FileChannel - fileChannel.close(); - } catch (final IOException e) { - // Ignore - } - fileChannel = null; + try { + // Closing raf will also close the associated FileChannel + fileChannel.close(); + } catch (final IOException e) { + // Ignore } + fileChannel = null; } fileChannel = null; nestedJarHandler.markSliceAsClosed(this); diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 723c943e1..64bab42ff 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -385,10 +385,8 @@ private Method findMethod(final Class cls, final Object obj, final String met // (may result in a reflective access warning on stderr) if (found) { for (final Method method : methodsForName) { - if (Arrays.equals(method.getParameterTypes(), paramTypes)) { - if (makeAccessible(obj, method)) { - return method; - } + if (Arrays.equals(method.getParameterTypes(), paramTypes) && makeAccessible(obj, method)) { + return method; } } } From df4739b521f92a80f92abb6e79ab7017dda4fe12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 03:12:55 -0600 Subject: [PATCH 388/713] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 672d07bcf..b186e8be0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ClassGraph is an uber-fast parallelized classpath scanner and module scanner for [![GitHub issues](https://img.shields.io/github/issues/classgraph/classgraph.svg)](https://github.com/classgraph/classgraph/issues/) [![lgtm alerts](https://img.shields.io/lgtm/alerts/g/classgraph/classgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/classgraph/classgraph/alerts/) [![lgtm code quality](https://img.shields.io/lgtm/grade/java/g/classgraph/classgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/classgraph/classgraph/context:java) -[![Codacy Badge](https://img.shields.io/codacy/grade/31d639dd3874454c917f80ea2bff8155.svg?style=flat)](https://www.codacy.com/app/classgraph/classgraph) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/ebc65f685d504cfcb379533d28d6353c)](https://www.codacy.com/gh/classgraph/classgraph/dashboard?utm_source=github.com&utm_medium=referral&utm_content=classgraph/classgraph&utm_campaign=Badge_Grade)
[![Dependencies: none](https://img.shields.io/badge/dependencies-none-blue.svg)](#) [![Dependents](https://badgen.net/github/dependents-repo/classgraph/classgraph)](https://github.com/classgraph/classgraph/network/dependents?package_id=UGFja2FnZS0xODcxNTE4NTM%3D) From a223707a56b9f17132ca70f9d557ea7e1945f55f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 19:04:05 -0600 Subject: [PATCH 389/713] Further work on #673 --- .../github/classgraph/ClasspathElement.java | 20 +- .../classgraph/ClasspathElementFileDir.java | 54 ++--- .../classgraph/ClasspathElementModule.java | 12 +- .../classgraph/ClasspathElementPathDir.java | 61 +++--- .../classgraph/ClasspathElementZip.java | 26 +-- .../java/io/github/classgraph/Resource.java | 5 +- .../java/io/github/classgraph/Scanner.java | 190 ++++++++++-------- .../classgraph/classpath/ClasspathOrder.java | 60 ++---- .../issues/issue46/Issue46Test.java | 16 +- 9 files changed, 210 insertions(+), 234 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index a4c6c6e18..683d07d36 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -38,8 +38,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -77,7 +75,7 @@ abstract class ClasspathElement implements Comparable { * a Class-Path entry in the manifest). Set to -1 initially in case the same ClasspathElement is present twice * in the classpath, as a child of different parent ClasspathElements. */ - final AtomicInteger classpathElementIdxWithinParent = new AtomicInteger(-1); + final int classpathElementIdxWithinParent; /** * The child classpath elements, keyed by the order of the child classpath element within the Class-Path entry @@ -105,7 +103,10 @@ abstract class ClasspathElement implements Comparable { protected final AtomicBoolean scanned = new AtomicBoolean(false); /** The classloader that this classpath element was obtained from. */ - protected AtomicReference classLoader = new AtomicReference<>(); + protected ClassLoader classLoader; + + /** The package root within the jarfile or Path. */ + protected String packageRootPrefix; /** * The name of the module from the {@code module-info.class} module descriptor, if one is present in the root of @@ -126,16 +127,19 @@ abstract class ClasspathElement implements Comparable { * @param scanSpec * the scan spec */ - ClasspathElement(final ScanSpec scanSpec) { + ClasspathElement(final ClasspathEntryWorkUnit workUnit, final ScanSpec scanSpec) { + this.packageRootPrefix = workUnit.packageRootPrefix; + this.classpathElementIdxWithinParent = workUnit.classpathElementIdxWithinParent; + this.classLoader = workUnit.classLoader; this.scanSpec = scanSpec; } // ------------------------------------------------------------------------------------------------------------- - // Sort in increasing order of classpathElementIdxWithinParent + /** Sort in increasing order of classpathElementIdxWithinParent. */ @Override public int compareTo(final ClasspathElement other) { - return this.classpathElementIdxWithinParent.get() - other.classpathElementIdxWithinParent.get(); + return this.classpathElementIdxWithinParent - other.classpathElementIdxWithinParent; } // ------------------------------------------------------------------------------------------------------------- @@ -146,7 +150,7 @@ public int compareTo(final ClasspathElement other) { * @return the classloader */ ClassLoader getClassLoader() { - return classLoader.get(); + return classLoader; } /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 703c21022..c9dc35e20 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -46,7 +46,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -64,9 +63,6 @@ class ClasspathElementFileDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final File classpathEltDir; - /** The directory at the root of the package hierarchy. */ - private final File packageRootDir; - /** Used to ensure that recursive scanning doesn't get into an infinite loop due to a link cycle. */ private final Set scannedCanonicalPaths = new HashSet<>(); @@ -78,16 +74,17 @@ class ClasspathElementFileDir extends ClasspathElement { * * @param classpathEltDir * the classpath element directory + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementFileDir(final File classpathEltDir, final String packageRootPrefix, + ClasspathElementFileDir(final File classpathEltDir, final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + super(workUnit, scanSpec); this.classpathEltDir = classpathEltDir; - this.packageRootDir = new File(classpathEltDir, packageRootPrefix); this.nestedJarHandler = nestedJarHandler; } @@ -121,28 +118,27 @@ void open(final WorkQueue workQueue, final LogNode log) if (log != null) { log(classpathElementIdx, "Found lib jar: " + file, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(file.getPath(), getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(file.getPath(), getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + /* packageRootPrefix = */ "")); } } } } } - // Only look for package roots if the package root is the root of the classpath element - if (packageRootDir.equals(classpathEltDir)) { + // Only look for package roots if the package root is empty + if (packageRootPrefix.isEmpty()) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { final File packageRoot = new File(classpathEltDir, packageRootPrefix); if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { log(classpathElementIdx, "Found package root: " + packageRoot, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(classpathEltDir, packageRootPrefix, - getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(packageRoot, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + packageRootPrefix)); } } } @@ -186,14 +182,7 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - // Relativize resource file to classpath element dir - final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); - String pathRelativeToClasspathElt = FastPathResolver - .resolve(resourceFile.getPath().substring(classpathEltDir.getPath().length())); - while (pathRelativeToClasspathElt.startsWith("/")) { - pathRelativeToClasspathElt = pathRelativeToClasspathElt.substring(1); - } - return pathRelativeToClasspathElt; + return packageRootPrefix.isEmpty() ? getPath() : packageRootPrefix + getPath(); } @Override @@ -303,7 +292,7 @@ public void close() { */ @Override Resource getResource(final String pathRelativeToPackageRoot) { - final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); + final File resourceFile = new File(classpathEltDir, pathRelativeToPackageRoot); return FileUtils.canReadAndIsFile(resourceFile) ? newResource(pathRelativeToPackageRoot, resourceFile, nestedJarHandler) : null; @@ -340,7 +329,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { } final String dirPath = dir.getPath(); - final int ignorePrefixLen = packageRootDir.getPath().length() + 1; + final int ignorePrefixLen = classpathEltDir.getPath().length() + 1; final String dirRelativePath = ignorePrefixLen > dirPath.length() ? "/" // : dirPath.substring(ignorePrefixLen).replace(File.separatorChar, '/') + "/"; final boolean isDefaultPackage = "/".equals(dirRelativePath); @@ -490,9 +479,9 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log(classpathElementIdx, "Scanning directory classpath element " + packageRootDir, log); + : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltDir, log); - scanDirRecursively(packageRootDir, subLog); + scanDirRecursively(classpathEltDir, subLog); finishScanPaths(subLog); } @@ -523,7 +512,7 @@ public File getFile() { */ @Override URI getURI() { - return packageRootDir.toURI(); + return classpathEltDir.toURI(); } @Override @@ -538,7 +527,7 @@ List getAllURIs() { */ @Override public String toString() { - return packageRootDir.toString(); + return classpathEltDir.toString(); } /* (non-Javadoc) @@ -546,7 +535,7 @@ public String toString() { */ @Override public int hashCode() { - return Objects.hash(classpathEltDir, packageRootDir); + return Objects.hash(classpathEltDir); } /* (non-Javadoc) @@ -560,7 +549,6 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementFileDir other = (ClasspathElementFileDir) obj; - return Objects.equals(this.classpathEltDir, other.classpathEltDir) - && Objects.equals(this.packageRootDir, other.packageRootDir); + return Objects.equals(this.classpathEltDir, other.classpathEltDir); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 209469fa6..70d197fd8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -81,6 +81,8 @@ class ClasspathElementModule extends ClasspathElement { * * @param moduleRef * the module ref + * @param workUnit + * the work unit * @param moduleRefToModuleReaderProxyRecyclerMap * the module ref to module reader proxy recycler map * @param scanSpec @@ -88,8 +90,9 @@ class ClasspathElementModule extends ClasspathElement { */ ClasspathElementModule(final ModuleRef moduleRef, final SingletonMap, IOException> // - moduleRefToModuleReaderProxyRecyclerMap, final ScanSpec scanSpec) { - super(scanSpec); + moduleRefToModuleReaderProxyRecyclerMap, final ClasspathEntryWorkUnit workUnit, + final ScanSpec scanSpec) { + super(workUnit, scanSpec); this.moduleRefToModuleReaderProxyRecyclerMap = moduleRefToModuleReaderProxyRecyclerMap; this.moduleRef = moduleRef; } @@ -141,11 +144,6 @@ public String getPath() { return resourcePath; } - @Override - public String getPathRelativeToClasspathElement() { - return resourcePath; - } - @Override public long getLastModified() { return 0L; // Unknown diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 604263dc5..73dc33678 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -50,7 +49,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -68,9 +66,6 @@ class ClasspathElementPathDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final Path classpathEltPath; - /** The package root. */ - private final Path packageRootPath; - /** Used to ensure that recursive scanning doesn't get into an infinite loop due to a link cycle. */ private final Set scannedCanonicalPaths = new HashSet<>(); @@ -82,16 +77,17 @@ class ClasspathElementPathDir extends ClasspathElement { * * @param classpathEltPath * the classpath element {@link Path} + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementPathDir(final Path classpathEltPath, final String packageRoot, + ClasspathElementPathDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + super(workUnit, scanSpec); this.classpathEltPath = classpathEltPath; - this.packageRootPath = classpathEltPath.resolve(packageRoot); this.nestedJarHandler = nestedJarHandler; } @@ -122,10 +118,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (log != null) { log(classpathElementIdx, "Found lib jar: " + filePath, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(filePath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(filePath, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + /* packageRootPrefix = */ "")); } } } catch (final IOException e) { @@ -133,19 +129,18 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - // Only look for package roots if the package root is the root of the classpath element - if (packageRootPath.equals(classpathEltPath)) { + // Only look for package roots if the package root is empty + if (packageRootPrefix.isEmpty()) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { final Path packageRoot = classpathEltPath.resolve(packageRootPrefix); if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { log(classpathElementIdx, "Found package root: " + packageRootPrefix, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(classpathEltPath, packageRootPrefix, - getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(packageRoot, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + packageRootPrefix)); } } } @@ -183,7 +178,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes @Override public String getPath() { - String path = FastPathResolver.resolve(packageRootPath.relativize(resourcePath).toString()); + String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); while (path.startsWith("/")) { path = path.substring(1); } @@ -192,11 +187,7 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); - while (path.startsWith("/")) { - path = path.substring(1); - } - return path; + return packageRootPrefix.isEmpty() ? getPath() : packageRootPrefix + getPath(); } @Override @@ -310,7 +301,7 @@ public void close() { */ @Override Resource getResource(final String relativePath) { - final Path resourcePath = packageRootPath.resolve(relativePath); + final Path resourcePath = classpathEltPath.resolve(relativePath); return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, nestedJarHandler) : null; } @@ -344,7 +335,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } - String dirRelativePathStr = FastPathResolver.resolve(packageRootPath.relativize(path).toString()); + String dirRelativePathStr = FastPathResolver.resolve(classpathEltPath.relativize(path).toString()); while (dirRelativePathStr.startsWith("/")) { dirRelativePathStr = dirRelativePathStr.substring(1); } @@ -525,7 +516,7 @@ void scanPaths(final LogNode log) { final LogNode subLog = log == null ? null : log(classpathElementIdx, "Scanning Path classpath element " + getURI(), log); - scanPathRecursively(packageRootPath, subLog); + scanPathRecursively(classpathEltPath, subLog); finishScanPaths(subLog); } @@ -562,13 +553,9 @@ public File getFile() { @Override URI getURI() { try { - return packageRootPath.toUri(); + return classpathEltPath.toUri(); } catch (IOError | SecurityException e) { - try { - return new URI("file:" + packageRootPath); - } catch (final URISyntaxException e1) { - throw new IllegalArgumentException("Could not convert to URI: " + packageRootPath); - } + throw new IllegalArgumentException("Could not convert to URI: " + classpathEltPath); } } @@ -585,9 +572,10 @@ List getAllURIs() { @Override public String toString() { try { - return packageRootPath.toUri().toString(); + // Path.toString() does not include the URI scheme for some reason + return classpathEltPath.toUri().toString(); } catch (IOError | SecurityException e) { - return packageRootPath.toString(); + return classpathEltPath.toString(); } } @@ -596,7 +584,7 @@ public String toString() { */ @Override public int hashCode() { - return Objects.hash(classpathEltPath, packageRootPath); + return Objects.hash(classpathEltPath); } /* (non-Javadoc) @@ -610,7 +598,6 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementPathDir other = (ClasspathElementPathDir) obj; - return Objects.equals(this.classpathEltPath, other.classpathEltPath) - && Objects.equals(this.packageRootPath, other.packageRootPath); + return Objects.equals(this.classpathEltPath, other.classpathEltPath); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index da29b3dc7..7c0c3541f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -49,7 +49,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -76,8 +75,6 @@ class ClasspathElementZip extends ClasspathElement { private final String rawPath; /** The logical zipfile for this classpath element. */ LogicalZipFile logicalZipFile; - /** The package root within the jarfile. */ - private String packageRootPrefix = ""; /** The normalized path of the jarfile, "!/"-separated if nested, excluding any package root. */ private String zipFilePath; /** A map from relative path to {@link Resource} for non-rejected zip entries. */ @@ -100,13 +97,16 @@ class ClasspathElementZip extends ClasspathElement { * @param rawPathObj * the raw path to the jarfile as a {@link String}, possibly including "!"-delimited nested paths, or * a {@link URL}, {@link URI} ol {@link Path} for the jarfile. + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementZip(final Object rawPathObj, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + ClasspathElementZip(final Object rawPathObj, final ClasspathEntryWorkUnit workUnit, + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + super(workUnit, scanSpec); // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; @@ -217,11 +217,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (subLog != null) { subLog.log("Found nested lib jar: " + entryPath); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(entryPath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(entryPath, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); break; } } @@ -258,12 +257,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (scheduledChildClasspathElements.add(childClassPathEltPathWithPrefix)) { // Schedule child classpath element for scanning workQueue.addWorkUnit( // - new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(childClassPathEltPathWithPrefix, - getClassLoader()), + new ClasspathEntryWorkUnit(childClassPathEltPathWithPrefix, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); } } } @@ -289,11 +286,10 @@ void open(final WorkQueue workQueue, final LogNode log) // Only add child classpath elements once if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(childClassPathEltPath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(childClassPathEltPath, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); } } } diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 2445acf13..b7430850a 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -237,7 +237,10 @@ public String getContentAsString() throws IOException { * full path of {@code "BOOT-INF/classes/com/xyz/resource.xml"} or * {@code "META-INF/versions/11/com/xyz/resource.xml"}, not {@code "com/xyz/resource.xml"}. */ - public abstract String getPathRelativeToClasspathElement(); + public String getPathRelativeToClasspathElement() { + // Only overridden for jars + return getPath(); + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 26b3b0002..4520dc95c 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -62,7 +62,7 @@ import io.github.classgraph.Classfile.ClassfileFormatException; import io.github.classgraph.Classfile.SkipClassException; import nonapi.io.github.classgraph.classpath.ClasspathFinder; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathEntry; import nonapi.io.github.classgraph.classpath.ModuleFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; @@ -194,8 +194,9 @@ class Scanner implements Callable { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( systemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + new ClasspathEntryWorkUnit(null, defaultClassLoader, null, moduleOrder.size(), + ""), scanSpec); - classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -218,8 +219,9 @@ class Scanner implements Callable { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( nonSystemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + new ClasspathEntryWorkUnit(null, defaultClassLoader, null, moduleOrder.size(), + ""), scanSpec); - classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -324,30 +326,43 @@ private void processWorkUnits(final Collection workUnits, final LogNode l /** Used to enqueue classpath elements for opening. */ static class ClasspathEntryWorkUnit { - /** The raw classpath entry and associated {@link ClassLoader}. */ - private final ClasspathElementAndPackageRoot rawClasspathEntry; + /** The classpath entry object (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ + final Object classpathEntryObj; + + /** The classloader the classpath entry object was obtained from. */ + final ClassLoader classLoader; /** The parent classpath element. */ - private final ClasspathElement parentClasspathElement; + final ClasspathElement parentClasspathElement; /** The order within the parent classpath element. */ - private final int classpathElementIdxWithinParent; + final int classpathElementIdxWithinParent; + + /** The package root prefix (e.g. "BOOT-INF/classes/"). */ + final String packageRootPrefix; /** * Constructor. * - * @param rawClasspathEntry - * the raw classpath entry path and the classloader it was obtained from + * @param classpathEntryObj + * the raw classpath entry object + * @param classLoader + * the classloader the classpath entry object was obtained from * @param parentClasspathElement * the parent classpath element * @param classpathElementIdxWithinParent * the order within parent classpath element + * @param packageRootPrefix + * the package root prefix */ - public ClasspathEntryWorkUnit(final ClasspathElementAndPackageRoot rawClasspathEntry, - final ClasspathElement parentClasspathElement, final int classpathElementIdxWithinParent) { - this.rawClasspathEntry = rawClasspathEntry; + public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader classLoader, + final ClasspathElement parentClasspathElement, final int classpathElementIdxWithinParent, + final String packageRootPrefix) { + this.classpathEntryObj = classpathEntryObj; + this.classLoader = classLoader; this.parentClasspathElement = parentClasspathElement; this.classpathElementIdxWithinParent = classpathElementIdxWithinParent; + this.packageRootPrefix = packageRootPrefix; } } @@ -371,43 +386,48 @@ public ClasspathElement newInstance(final Object classpathEntryObj, final LogNod * The classpath element singleton map. For each classpath element path, canonicalize path, and create a * ClasspathElement singleton. */ - private final SingletonMap // - classpathEntryToClasspathElementSingletonMap = // - new SingletonMap() { + private final SingletonMap // + classpathEntryWorkUnitToClasspathElementSingletonMap = // + new SingletonMap() { @Override - public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpathEntry, + public ClasspathElement newInstance(final ClasspathEntryWorkUnit classpathEntryWorkUnit, final LogNode log) throws IOException, InterruptedException { - Object classpathEntryObj = classpathEntry.classpathElementObj; - if (classpathEntryObj == null) { + Object classpathEntObj = classpathEntryWorkUnit.classpathEntryObj; + if (classpathEntObj == null) { // Should not happen throw new IOException("Got null classpath entry object"); } - String dirOrPathPackageRoot = classpathEntry.dirOrPathPackageRoot; - while (dirOrPathPackageRoot.startsWith("/")) { - dirOrPathPackageRoot = dirOrPathPackageRoot.substring(1); - } - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end - if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { - final String classpathEntryStr = classpathEntryObj.toString(); + if (classpathEntObj instanceof URL || classpathEntObj instanceof URI) { + final String classpathEntryStr = classpathEntObj.toString(); if (classpathEntryStr.endsWith("!/")) { - classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); + classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); } else if (classpathEntryStr.endsWith("!")) { - classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); + classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); } } // If classpath entry object is a URL-formatted string, convert to a URL instance - if (classpathEntryObj instanceof String) { - final String classpathEntryStr = (String) classpathEntryObj; + if (classpathEntObj instanceof String) { + final String classpathEntryStr = (String) classpathEntObj; if (JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntryStr).matches()) { try { - classpathEntryObj = new URL(classpathEntryStr); + classpathEntObj = new URL(classpathEntryStr); + } catch (final MalformedURLException e) { + try { + classpathEntObj = new URI(classpathEntryStr); + } catch (final URISyntaxException e1) { + throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); + } + } + } else if (classpathEntryStr.contains("!")) { + try { + classpathEntObj = new URL("jar:file:" + classpathEntryStr); } catch (final MalformedURLException e) { try { - classpathEntryObj = new URI(classpathEntryStr); + classpathEntObj = new URI(classpathEntryStr); } catch (final URISyntaxException e1) { throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); } @@ -417,8 +437,9 @@ public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpa // Check type of classpath entry object Path classpathEntryPath = null; - if (classpathEntryObj instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntryObj; + if (classpathEntObj instanceof URL) { + final URL classpathEntryURL = (URL) classpathEntObj; + // TODO scheme can be "jar" final String scheme = classpathEntryURL.getProtocol(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) @@ -431,8 +452,8 @@ public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpa @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementZip(classpathEntryURL, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(classpathEntryURL, + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e) { @@ -470,7 +491,7 @@ public ClasspathElement newInstance(final Object key, public ClasspathElement newInstance(final Object key, final LogNode log) { return new ClasspathElementZip(classpathEntryURL, - nestedJarHandler, scanSpec); + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -478,12 +499,13 @@ public ClasspathElement newInstance(final Object key, } } } - } else if (classpathEntryObj instanceof URI) { - final URI classpathEntryURI = (URI) classpathEntryObj; + } else if (classpathEntObj instanceof URI) { + final URI classpathEntryURI = (URI) classpathEntObj; final String scheme = classpathEntryURI.getScheme(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryURI, nestedJarHandler, scanSpec); + return new ClasspathElementZip(classpathEntryURI, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } else { try { // See if the URI resolves to a file or directory via the Path API @@ -516,7 +538,7 @@ public ClasspathElement newInstance(final Object key, public ClasspathElement newInstance(final Object key, final LogNode log) { return new ClasspathElementZip(classpathEntryURI, - nestedJarHandler, scanSpec); + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -524,8 +546,8 @@ public ClasspathElement newInstance(final Object key, } } } - } else if (classpathEntryObj instanceof Path) { - classpathEntryPath = (Path) classpathEntryObj; + } else if (classpathEntObj instanceof Path) { + classpathEntryPath = (Path) classpathEntObj; } else { // Fall through for any other object type (toString will be used to get path) } @@ -538,52 +560,51 @@ public ClasspathElement newInstance(final Object key, } catch (final SecurityException e) { // Ignore } - final Path packageRootPath = classpathEntryPath.resolve(dirOrPathPackageRoot); - if (FileUtils.canReadAndIsFile(packageRootPath)) { + if (FileUtils.canReadAndIsFile(classpathEntryPath)) { // packageRootPath is a Path which points to a lib/ext jar inside a parent Path try { - return classpathEntryObjToClasspathEntrySingletonMap.get(packageRootPath, log, + final Path path = classpathEntryPath; + return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, new NewInstanceFactory() { @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementZip(packageRootPath, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(path, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); } - } else if (FileUtils.canReadAndIsDir(packageRootPath)) { + } else if (FileUtils.canReadAndIsDir(classpathEntryPath)) { if ("JrtFileSystem" - .equals(packageRootPath.getFileSystem().getClass().getSimpleName())) { + .equals(classpathEntryPath.getFileSystem().getClass().getSimpleName())) { // Ignore JrtFileSystem (#553) -- paths are of form: // /modules/java.base/module-info.class - throw new IOException("Ignoring JrtFS filesystem path " + packageRootPath + throw new IOException("Ignoring JrtFS filesystem path " + classpathEntryPath + " (modules are scanned using the JPMS API)"); } // classpathEntryObj is a Path which points to a dir -- need to scan it recursively try { final Path path = classpathEntryPath; - final String root = dirOrPathPackageRoot; return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, new NewInstanceFactory() { @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementPathDir(path, root, nestedJarHandler, - scanSpec); + return new ClasspathElementPathDir(path, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); } } } // Fall through for other object types (including String) // Convert classpathEntryObj to a string - final String classpathEntryPathStr = classpathEntryObj.toString(); + final String classpathEntryPathStr = classpathEntObj.toString(); // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators final String pathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), @@ -622,7 +643,6 @@ public ClasspathElement newInstance(final Object key, } try { final boolean jar = isJar; - final String root = dirOrPathPackageRoot; return classpathEntryObjToClasspathEntrySingletonMap.get(pathCanonicalized, log, new NewInstanceFactory() { @Override @@ -630,10 +650,10 @@ public ClasspathElement newInstance(final Object key, final LogNode log) { // Instantiate a ClasspathElementZip or ClasspathElementDir singleton // for the classpath element path return jar - ? new ClasspathElementZip(pathCanonicalized, nestedJarHandler, - scanSpec) - : new ClasspathElementFileDir(fileCanonicalized, root, - nestedJarHandler, scanSpec); + ? new ClasspathElementZip(pathCanonicalized, classpathEntryWorkUnit, + nestedJarHandler, scanSpec) + : new ClasspathElementFileDir(fileCanonicalized, + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -644,7 +664,7 @@ public ClasspathElement newInstance(final Object key, final LogNode log) { /** * Create a WorkUnitProcessor for opening traditional classpath entries (which are mapped to - * {@link ClasspathElementFileDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled + * {@link ClasspathElementPathDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * * @param allClasspathElts @@ -666,21 +686,14 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // Create a ClasspathElementZip or ClasspathElementDir for each entry in the classpath ClasspathElement classpathElt; try { - classpathElt = classpathEntryToClasspathElementSingletonMap.get(workUnit.rawClasspathEntry, - log); - // Set index within parent, if it hasn't already been set - classpathElt.classpathElementIdxWithinParent.compareAndSet(-1, - workUnit.classpathElementIdxWithinParent); - // Set classloader, if it hasn't already been set - classpathElt.classLoader.compareAndSet(null, workUnit.rawClasspathEntry.classLoader); + // Get or create ClasspathElement from raw classpath entry object + classpathElt = classpathEntryWorkUnitToClasspathElementSingletonMap.get(workUnit, log); } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for classpath entry " - + workUnit.rawClasspathEntry + " : " + (e.getCause() == null ? e : e.getCause())); + + workUnit.classpathEntryObj + " : " + (e.getCause() == null ? e : e.getCause())); } - // Only run open() once per ClasspathElement (it is possible for there to be - // multiple classpath elements with different non-canonical paths that map to - // the same canonical path, i.e. to the same ClasspathElement) + // Only run open() once per ClasspathElement if (allClasspathElts.add(classpathElt)) { final LogNode subLog = log == null ? null : log.log("Opening classpath element " + classpathElt); @@ -701,11 +714,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, } } catch (final IOException | SecurityException e) { if (log != null) { - log.log("Skipping invalid classpath element " - + workUnit.rawClasspathEntry.classpathElementObj - + (workUnit.rawClasspathEntry.dirOrPathPackageRoot.isEmpty() ? "" - : "/" + workUnit.rawClasspathEntry.dirOrPathPackageRoot) - + " : " + e); + log.log("Skipping invalid classpath element " + workUnit.classpathEntryObj + " : " + e); } } } @@ -931,9 +940,12 @@ private void preprocessClasspathElementsByType(final List fina final List> classpathEltDirs = new ArrayList<>(); final List> classpathEltZips = new ArrayList<>(); for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { - if (classpathElt instanceof ClasspathElementFileDir) { - // Separate out ClasspathElementDir elements from other types - classpathEltDirs.add(new SimpleEntry<>(classpathElt.getFile().getPath(), classpathElt)); + if (classpathElt instanceof ClasspathElementFileDir + || classpathElt instanceof ClasspathElementPathDir) { + // Separate out ClasspathElementFileDir and ClasspathElementPathDir elements from other types + final File file = classpathElt.getFile(); + final String path = file == null ? classpathElt.toString() : file.getPath(); + classpathEltDirs.add(new SimpleEntry<>(path, classpathElt)); } else if (classpathElt instanceof ClasspathElementZip) { // Separate out ClasspathElementZip elements from other types @@ -1128,14 +1140,14 @@ private ScanResult performScan(final List finalClasspathEltOrd private ScanResult openClasspathElementsThenScan() throws InterruptedException, ExecutionException { // Get order of elements in traditional classpath final List rawClasspathEntryWorkUnits = new ArrayList<>(); - final List rawClasspathOrder = classpathFinder.getClasspathOrder() - .getOrder(); - for (final ClasspathElementAndPackageRoot rawClasspathEntry : rawClasspathOrder) { - rawClasspathEntryWorkUnits - .add(new ClasspathEntryWorkUnit(rawClasspathEntry, /* parentClasspathElement = */ null, - // classpathElementIdxWithinParent is the original classpath index, - // for toplevel classpath elements - /* classpathElementIdxWithinParent = */ rawClasspathEntryWorkUnits.size())); + final List rawClasspathOrder = classpathFinder.getClasspathOrder().getOrder(); + for (final ClasspathEntry rawClasspathEntry : rawClasspathOrder) { + rawClasspathEntryWorkUnits.add(new ClasspathEntryWorkUnit(rawClasspathEntry.classpathEntryObj, + rawClasspathEntry.classLoader, /* parentClasspathElement = */ null, + // classpathElementIdxWithinParent is the original classpath index, + // for toplevel classpath elements + /* classpathElementIdxWithinParent = */ rawClasspathEntryWorkUnits.size(), + /* packageRootPrefix = */ "")); } // In parallel, create a ClasspathElement singleton for each classpath element, then call open() diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index e43fad158..ecbe58e11 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -62,7 +62,7 @@ public class ClasspathOrder { private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); /** The classpath order. Keys are instances of {@link String} or {@link URL}. */ - private final List order = new ArrayList<>(); + private final List order = new ArrayList<>(); /** Suffixes for automatic package roots, e.g. "!/BOOT-INF/classes". */ private static final List AUTOMATIC_PACKAGE_ROOT_SUFFIXES = new ArrayList<>(); @@ -76,71 +76,45 @@ public class ClasspathOrder { /** * A classpath element and the {@link ClassLoader} it was obtained from. */ - public static class ClasspathElementAndPackageRoot { - /** - * The classpath element root (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). - */ - public final Object classpathElementObj; - - /** The classpath element package root, prefix, e.g. "BOOT-INF/classes" or "". */ - public final String dirOrPathPackageRoot; + public static class ClasspathEntry { + /** The classpath entry object (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ + public final Object classpathEntryObj; /** The classloader the classpath element was obtained from. */ public final ClassLoader classLoader; - /** - * Constructor for directory or {@link Path} classpath entries. - * - * @param classpathElementObj - * the classpath element object (a {@link String} path, {@link Path}, {@link URL} or - * {@link URI}). - * @param dirOrPathPackageRoot - * the classpath element package root prefix, e.g. "BOOT-INF/classes" or "". Only used for - * directory or {@link Path} classpath entries. - * @param classLoader - * the classloader the classpath element was obtained from. - */ - public ClasspathElementAndPackageRoot(final Object classpathElementObj, final String dirOrPathPackageRoot, - final ClassLoader classLoader) { - this.classpathElementObj = classpathElementObj; - this.dirOrPathPackageRoot = dirOrPathPackageRoot; - this.classLoader = classLoader; - } - /** * Constructor. * - * @param classpathElementRoot - * the classpath element root (a {@link String} or {@link URL} or {@link Path}). + * @param classpathEntryObj + * the classpath entry object (a {@link String} or {@link URL} or {@link Path}). * @param classLoader * the classloader the classpath element was obtained from. */ - public ClasspathElementAndPackageRoot(final Object classpathElementRoot, final ClassLoader classLoader) { - this.classpathElementObj = classpathElementRoot; - this.dirOrPathPackageRoot = ""; + public ClasspathEntry(final Object classpathEntryObj, final ClassLoader classLoader) { + this.classpathEntryObj = classpathEntryObj; this.classLoader = classLoader; } @Override public int hashCode() { - return Objects.hash(classpathElementObj, dirOrPathPackageRoot, classLoader); + return Objects.hash(classpathEntryObj); } @Override public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ClasspathElementAndPackageRoot)) { + } else if (!(obj instanceof ClasspathEntry)) { return false; } - final ClasspathElementAndPackageRoot other = (ClasspathElementAndPackageRoot) obj; - return Objects.equals(this.dirOrPathPackageRoot, other.dirOrPathPackageRoot) - && Objects.equals(this.classpathElementObj, other.classpathElementObj); + final ClasspathEntry other = (ClasspathEntry) obj; + return Objects.equals(this.classpathEntryObj, other.classpathEntryObj); } @Override public String toString() { - return classpathElementObj + " [" + classLoader + "]"; + return classpathEntryObj + " [" + classLoader + "]"; } } @@ -159,7 +133,7 @@ public String toString() { * * @return the classpath order. */ - public List getOrder() { + public List getOrder() { return order; } @@ -208,7 +182,7 @@ private boolean filter(final URL classpathElementURL, final String classpathElem */ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classLoader) { if (classpathEntryUniqueResolvedPaths.add(pathEntry)) { - order.add(new ClasspathElementAndPackageRoot(pathEntry, classLoader)); + order.add(new ClasspathEntry(pathEntry, classLoader)); return true; } return false; @@ -260,7 +234,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle // Deduplicate classpath elements if (classpathEntryUniqueResolvedPaths.add(pathElementStrWithoutSuffix)) { // Record classpath element in classpath order - order.add(new ClasspathElementAndPackageRoot(pathElementWithoutSuffix, classLoader)); + order.add(new ClasspathEntry(pathElementWithoutSuffix, classLoader)); return true; } } else { @@ -274,7 +248,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle return false; } if (classpathEntryUniqueResolvedPaths.add(pathElementStrResolved)) { - order.add(new ClasspathElementAndPackageRoot(pathElementStrResolved, classLoader)); + order.add(new ClasspathEntry(pathElementStrResolved, classLoader)); return true; } } diff --git a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java index a76cb62fa..d62d33735 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -30,6 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.MalformedURLException; +import java.net.URL; + import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -47,8 +50,19 @@ public void issue46Test() { final String jarPath = "jar:file://" + Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; - try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().verbose() + .scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } +// +// public static void main(String[] args) throws MalformedURLException { +// final String jarPath = "jar:file:" + +// Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() +// + "!/level2.jar!/level3.jar!/classpath1/classpath2"; +// System.out.println(jarPath); +// URL u = new URL(jarPath); +// System.out.println(u); +// System.out.println(u.getPath()); +// } } From 374cbce32216cceb63d1b8bdc3679e09eaed6b82 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 19:04:05 -0600 Subject: [PATCH 390/713] Final fixes for #673 --- .../github/classgraph/ClasspathElement.java | 20 +- .../classgraph/ClasspathElementFileDir.java | 54 ++--- .../classgraph/ClasspathElementModule.java | 12 +- .../classgraph/ClasspathElementPathDir.java | 61 +++--- .../classgraph/ClasspathElementZip.java | 26 +-- .../java/io/github/classgraph/Resource.java | 5 +- .../java/io/github/classgraph/Scanner.java | 190 ++++++++++-------- .../classgraph/classpath/ClasspathOrder.java | 60 ++---- .../issues/issue46/Issue46Test.java | 16 +- 9 files changed, 210 insertions(+), 234 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index a4c6c6e18..683d07d36 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -38,8 +38,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -77,7 +75,7 @@ abstract class ClasspathElement implements Comparable { * a Class-Path entry in the manifest). Set to -1 initially in case the same ClasspathElement is present twice * in the classpath, as a child of different parent ClasspathElements. */ - final AtomicInteger classpathElementIdxWithinParent = new AtomicInteger(-1); + final int classpathElementIdxWithinParent; /** * The child classpath elements, keyed by the order of the child classpath element within the Class-Path entry @@ -105,7 +103,10 @@ abstract class ClasspathElement implements Comparable { protected final AtomicBoolean scanned = new AtomicBoolean(false); /** The classloader that this classpath element was obtained from. */ - protected AtomicReference classLoader = new AtomicReference<>(); + protected ClassLoader classLoader; + + /** The package root within the jarfile or Path. */ + protected String packageRootPrefix; /** * The name of the module from the {@code module-info.class} module descriptor, if one is present in the root of @@ -126,16 +127,19 @@ abstract class ClasspathElement implements Comparable { * @param scanSpec * the scan spec */ - ClasspathElement(final ScanSpec scanSpec) { + ClasspathElement(final ClasspathEntryWorkUnit workUnit, final ScanSpec scanSpec) { + this.packageRootPrefix = workUnit.packageRootPrefix; + this.classpathElementIdxWithinParent = workUnit.classpathElementIdxWithinParent; + this.classLoader = workUnit.classLoader; this.scanSpec = scanSpec; } // ------------------------------------------------------------------------------------------------------------- - // Sort in increasing order of classpathElementIdxWithinParent + /** Sort in increasing order of classpathElementIdxWithinParent. */ @Override public int compareTo(final ClasspathElement other) { - return this.classpathElementIdxWithinParent.get() - other.classpathElementIdxWithinParent.get(); + return this.classpathElementIdxWithinParent - other.classpathElementIdxWithinParent; } // ------------------------------------------------------------------------------------------------------------- @@ -146,7 +150,7 @@ public int compareTo(final ClasspathElement other) { * @return the classloader */ ClassLoader getClassLoader() { - return classLoader.get(); + return classLoader; } /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 703c21022..c9dc35e20 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -46,7 +46,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -64,9 +63,6 @@ class ClasspathElementFileDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final File classpathEltDir; - /** The directory at the root of the package hierarchy. */ - private final File packageRootDir; - /** Used to ensure that recursive scanning doesn't get into an infinite loop due to a link cycle. */ private final Set scannedCanonicalPaths = new HashSet<>(); @@ -78,16 +74,17 @@ class ClasspathElementFileDir extends ClasspathElement { * * @param classpathEltDir * the classpath element directory + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementFileDir(final File classpathEltDir, final String packageRootPrefix, + ClasspathElementFileDir(final File classpathEltDir, final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + super(workUnit, scanSpec); this.classpathEltDir = classpathEltDir; - this.packageRootDir = new File(classpathEltDir, packageRootPrefix); this.nestedJarHandler = nestedJarHandler; } @@ -121,28 +118,27 @@ void open(final WorkQueue workQueue, final LogNode log) if (log != null) { log(classpathElementIdx, "Found lib jar: " + file, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(file.getPath(), getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(file.getPath(), getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + /* packageRootPrefix = */ "")); } } } } } - // Only look for package roots if the package root is the root of the classpath element - if (packageRootDir.equals(classpathEltDir)) { + // Only look for package roots if the package root is empty + if (packageRootPrefix.isEmpty()) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { final File packageRoot = new File(classpathEltDir, packageRootPrefix); if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { log(classpathElementIdx, "Found package root: " + packageRoot, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(classpathEltDir, packageRootPrefix, - getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(packageRoot, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + packageRootPrefix)); } } } @@ -186,14 +182,7 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - // Relativize resource file to classpath element dir - final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); - String pathRelativeToClasspathElt = FastPathResolver - .resolve(resourceFile.getPath().substring(classpathEltDir.getPath().length())); - while (pathRelativeToClasspathElt.startsWith("/")) { - pathRelativeToClasspathElt = pathRelativeToClasspathElt.substring(1); - } - return pathRelativeToClasspathElt; + return packageRootPrefix.isEmpty() ? getPath() : packageRootPrefix + getPath(); } @Override @@ -303,7 +292,7 @@ public void close() { */ @Override Resource getResource(final String pathRelativeToPackageRoot) { - final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); + final File resourceFile = new File(classpathEltDir, pathRelativeToPackageRoot); return FileUtils.canReadAndIsFile(resourceFile) ? newResource(pathRelativeToPackageRoot, resourceFile, nestedJarHandler) : null; @@ -340,7 +329,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { } final String dirPath = dir.getPath(); - final int ignorePrefixLen = packageRootDir.getPath().length() + 1; + final int ignorePrefixLen = classpathEltDir.getPath().length() + 1; final String dirRelativePath = ignorePrefixLen > dirPath.length() ? "/" // : dirPath.substring(ignorePrefixLen).replace(File.separatorChar, '/') + "/"; final boolean isDefaultPackage = "/".equals(dirRelativePath); @@ -490,9 +479,9 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log(classpathElementIdx, "Scanning directory classpath element " + packageRootDir, log); + : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltDir, log); - scanDirRecursively(packageRootDir, subLog); + scanDirRecursively(classpathEltDir, subLog); finishScanPaths(subLog); } @@ -523,7 +512,7 @@ public File getFile() { */ @Override URI getURI() { - return packageRootDir.toURI(); + return classpathEltDir.toURI(); } @Override @@ -538,7 +527,7 @@ List getAllURIs() { */ @Override public String toString() { - return packageRootDir.toString(); + return classpathEltDir.toString(); } /* (non-Javadoc) @@ -546,7 +535,7 @@ public String toString() { */ @Override public int hashCode() { - return Objects.hash(classpathEltDir, packageRootDir); + return Objects.hash(classpathEltDir); } /* (non-Javadoc) @@ -560,7 +549,6 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementFileDir other = (ClasspathElementFileDir) obj; - return Objects.equals(this.classpathEltDir, other.classpathEltDir) - && Objects.equals(this.packageRootDir, other.packageRootDir); + return Objects.equals(this.classpathEltDir, other.classpathEltDir); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 209469fa6..70d197fd8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -81,6 +81,8 @@ class ClasspathElementModule extends ClasspathElement { * * @param moduleRef * the module ref + * @param workUnit + * the work unit * @param moduleRefToModuleReaderProxyRecyclerMap * the module ref to module reader proxy recycler map * @param scanSpec @@ -88,8 +90,9 @@ class ClasspathElementModule extends ClasspathElement { */ ClasspathElementModule(final ModuleRef moduleRef, final SingletonMap, IOException> // - moduleRefToModuleReaderProxyRecyclerMap, final ScanSpec scanSpec) { - super(scanSpec); + moduleRefToModuleReaderProxyRecyclerMap, final ClasspathEntryWorkUnit workUnit, + final ScanSpec scanSpec) { + super(workUnit, scanSpec); this.moduleRefToModuleReaderProxyRecyclerMap = moduleRefToModuleReaderProxyRecyclerMap; this.moduleRef = moduleRef; } @@ -141,11 +144,6 @@ public String getPath() { return resourcePath; } - @Override - public String getPathRelativeToClasspathElement() { - return resourcePath; - } - @Override public long getLastModified() { return 0L; // Unknown diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 604263dc5..73dc33678 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -50,7 +49,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -68,9 +66,6 @@ class ClasspathElementPathDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final Path classpathEltPath; - /** The package root. */ - private final Path packageRootPath; - /** Used to ensure that recursive scanning doesn't get into an infinite loop due to a link cycle. */ private final Set scannedCanonicalPaths = new HashSet<>(); @@ -82,16 +77,17 @@ class ClasspathElementPathDir extends ClasspathElement { * * @param classpathEltPath * the classpath element {@link Path} + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementPathDir(final Path classpathEltPath, final String packageRoot, + ClasspathElementPathDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + super(workUnit, scanSpec); this.classpathEltPath = classpathEltPath; - this.packageRootPath = classpathEltPath.resolve(packageRoot); this.nestedJarHandler = nestedJarHandler; } @@ -122,10 +118,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (log != null) { log(classpathElementIdx, "Found lib jar: " + filePath, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(filePath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(filePath, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + /* packageRootPrefix = */ "")); } } } catch (final IOException e) { @@ -133,19 +129,18 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - // Only look for package roots if the package root is the root of the classpath element - if (packageRootPath.equals(classpathEltPath)) { + // Only look for package roots if the package root is empty + if (packageRootPrefix.isEmpty()) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { final Path packageRoot = classpathEltPath.resolve(packageRootPrefix); if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { log(classpathElementIdx, "Found package root: " + packageRootPrefix, log); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(classpathEltPath, packageRootPrefix, - getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(packageRoot, getClassLoader(), /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + packageRootPrefix)); } } } @@ -183,7 +178,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes @Override public String getPath() { - String path = FastPathResolver.resolve(packageRootPath.relativize(resourcePath).toString()); + String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); while (path.startsWith("/")) { path = path.substring(1); } @@ -192,11 +187,7 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); - while (path.startsWith("/")) { - path = path.substring(1); - } - return path; + return packageRootPrefix.isEmpty() ? getPath() : packageRootPrefix + getPath(); } @Override @@ -310,7 +301,7 @@ public void close() { */ @Override Resource getResource(final String relativePath) { - final Path resourcePath = packageRootPath.resolve(relativePath); + final Path resourcePath = classpathEltPath.resolve(relativePath); return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, nestedJarHandler) : null; } @@ -344,7 +335,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } - String dirRelativePathStr = FastPathResolver.resolve(packageRootPath.relativize(path).toString()); + String dirRelativePathStr = FastPathResolver.resolve(classpathEltPath.relativize(path).toString()); while (dirRelativePathStr.startsWith("/")) { dirRelativePathStr = dirRelativePathStr.substring(1); } @@ -525,7 +516,7 @@ void scanPaths(final LogNode log) { final LogNode subLog = log == null ? null : log(classpathElementIdx, "Scanning Path classpath element " + getURI(), log); - scanPathRecursively(packageRootPath, subLog); + scanPathRecursively(classpathEltPath, subLog); finishScanPaths(subLog); } @@ -562,13 +553,9 @@ public File getFile() { @Override URI getURI() { try { - return packageRootPath.toUri(); + return classpathEltPath.toUri(); } catch (IOError | SecurityException e) { - try { - return new URI("file:" + packageRootPath); - } catch (final URISyntaxException e1) { - throw new IllegalArgumentException("Could not convert to URI: " + packageRootPath); - } + throw new IllegalArgumentException("Could not convert to URI: " + classpathEltPath); } } @@ -585,9 +572,10 @@ List getAllURIs() { @Override public String toString() { try { - return packageRootPath.toUri().toString(); + // Path.toString() does not include the URI scheme for some reason + return classpathEltPath.toUri().toString(); } catch (IOError | SecurityException e) { - return packageRootPath.toString(); + return classpathEltPath.toString(); } } @@ -596,7 +584,7 @@ public String toString() { */ @Override public int hashCode() { - return Objects.hash(classpathEltPath, packageRootPath); + return Objects.hash(classpathEltPath); } /* (non-Javadoc) @@ -610,7 +598,6 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementPathDir other = (ClasspathElementPathDir) obj; - return Objects.equals(this.classpathEltPath, other.classpathEltPath) - && Objects.equals(this.packageRootPath, other.packageRootPath); + return Objects.equals(this.classpathEltPath, other.classpathEltPath); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index da29b3dc7..7c0c3541f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -49,7 +49,6 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -76,8 +75,6 @@ class ClasspathElementZip extends ClasspathElement { private final String rawPath; /** The logical zipfile for this classpath element. */ LogicalZipFile logicalZipFile; - /** The package root within the jarfile. */ - private String packageRootPrefix = ""; /** The normalized path of the jarfile, "!/"-separated if nested, excluding any package root. */ private String zipFilePath; /** A map from relative path to {@link Resource} for non-rejected zip entries. */ @@ -100,13 +97,16 @@ class ClasspathElementZip extends ClasspathElement { * @param rawPathObj * the raw path to the jarfile as a {@link String}, possibly including "!"-delimited nested paths, or * a {@link URL}, {@link URI} ol {@link Path} for the jarfile. + * @param workUnit + * the work unit * @param nestedJarHandler * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementZip(final Object rawPathObj, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(scanSpec); + ClasspathElementZip(final Object rawPathObj, final ClasspathEntryWorkUnit workUnit, + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + super(workUnit, scanSpec); // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; @@ -217,11 +217,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (subLog != null) { subLog.log("Found nested lib jar: " + entryPath); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(entryPath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(entryPath, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); break; } } @@ -258,12 +257,10 @@ void open(final WorkQueue workQueue, final LogNode log) if (scheduledChildClasspathElements.add(childClassPathEltPathWithPrefix)) { // Schedule child classpath element for scanning workQueue.addWorkUnit( // - new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(childClassPathEltPathWithPrefix, - getClassLoader()), + new ClasspathEntryWorkUnit(childClassPathEltPathWithPrefix, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); } } } @@ -289,11 +286,10 @@ void open(final WorkQueue workQueue, final LogNode log) // Only add child classpath elements once if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - new ClasspathElementAndPackageRoot(childClassPathEltPath, getClassLoader()), + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(childClassPathEltPath, getClassLoader(), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + childClasspathEntryIdx++, /* packageRootPrefix = */ "")); } } } diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 2445acf13..b7430850a 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -237,7 +237,10 @@ public String getContentAsString() throws IOException { * full path of {@code "BOOT-INF/classes/com/xyz/resource.xml"} or * {@code "META-INF/versions/11/com/xyz/resource.xml"}, not {@code "com/xyz/resource.xml"}. */ - public abstract String getPathRelativeToClasspathElement(); + public String getPathRelativeToClasspathElement() { + // Only overridden for jars + return getPath(); + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 26b3b0002..4520dc95c 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -62,7 +62,7 @@ import io.github.classgraph.Classfile.ClassfileFormatException; import io.github.classgraph.Classfile.SkipClassException; import nonapi.io.github.classgraph.classpath.ClasspathFinder; -import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndPackageRoot; +import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathEntry; import nonapi.io.github.classgraph.classpath.ModuleFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; @@ -194,8 +194,9 @@ class Scanner implements Callable { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( systemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + new ClasspathEntryWorkUnit(null, defaultClassLoader, null, moduleOrder.size(), + ""), scanSpec); - classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -218,8 +219,9 @@ class Scanner implements Callable { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( nonSystemModuleRef, nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, + new ClasspathEntryWorkUnit(null, defaultClassLoader, null, moduleOrder.size(), + ""), scanSpec); - classpathElementModule.classLoader.compareAndSet(null, defaultClassLoader); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, classpathFinderLog); @@ -324,30 +326,43 @@ private void processWorkUnits(final Collection workUnits, final LogNode l /** Used to enqueue classpath elements for opening. */ static class ClasspathEntryWorkUnit { - /** The raw classpath entry and associated {@link ClassLoader}. */ - private final ClasspathElementAndPackageRoot rawClasspathEntry; + /** The classpath entry object (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ + final Object classpathEntryObj; + + /** The classloader the classpath entry object was obtained from. */ + final ClassLoader classLoader; /** The parent classpath element. */ - private final ClasspathElement parentClasspathElement; + final ClasspathElement parentClasspathElement; /** The order within the parent classpath element. */ - private final int classpathElementIdxWithinParent; + final int classpathElementIdxWithinParent; + + /** The package root prefix (e.g. "BOOT-INF/classes/"). */ + final String packageRootPrefix; /** * Constructor. * - * @param rawClasspathEntry - * the raw classpath entry path and the classloader it was obtained from + * @param classpathEntryObj + * the raw classpath entry object + * @param classLoader + * the classloader the classpath entry object was obtained from * @param parentClasspathElement * the parent classpath element * @param classpathElementIdxWithinParent * the order within parent classpath element + * @param packageRootPrefix + * the package root prefix */ - public ClasspathEntryWorkUnit(final ClasspathElementAndPackageRoot rawClasspathEntry, - final ClasspathElement parentClasspathElement, final int classpathElementIdxWithinParent) { - this.rawClasspathEntry = rawClasspathEntry; + public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader classLoader, + final ClasspathElement parentClasspathElement, final int classpathElementIdxWithinParent, + final String packageRootPrefix) { + this.classpathEntryObj = classpathEntryObj; + this.classLoader = classLoader; this.parentClasspathElement = parentClasspathElement; this.classpathElementIdxWithinParent = classpathElementIdxWithinParent; + this.packageRootPrefix = packageRootPrefix; } } @@ -371,43 +386,48 @@ public ClasspathElement newInstance(final Object classpathEntryObj, final LogNod * The classpath element singleton map. For each classpath element path, canonicalize path, and create a * ClasspathElement singleton. */ - private final SingletonMap // - classpathEntryToClasspathElementSingletonMap = // - new SingletonMap() { + private final SingletonMap // + classpathEntryWorkUnitToClasspathElementSingletonMap = // + new SingletonMap() { @Override - public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpathEntry, + public ClasspathElement newInstance(final ClasspathEntryWorkUnit classpathEntryWorkUnit, final LogNode log) throws IOException, InterruptedException { - Object classpathEntryObj = classpathEntry.classpathElementObj; - if (classpathEntryObj == null) { + Object classpathEntObj = classpathEntryWorkUnit.classpathEntryObj; + if (classpathEntObj == null) { // Should not happen throw new IOException("Got null classpath entry object"); } - String dirOrPathPackageRoot = classpathEntry.dirOrPathPackageRoot; - while (dirOrPathPackageRoot.startsWith("/")) { - dirOrPathPackageRoot = dirOrPathPackageRoot.substring(1); - } - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end - if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { - final String classpathEntryStr = classpathEntryObj.toString(); + if (classpathEntObj instanceof URL || classpathEntObj instanceof URI) { + final String classpathEntryStr = classpathEntObj.toString(); if (classpathEntryStr.endsWith("!/")) { - classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); + classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); } else if (classpathEntryStr.endsWith("!")) { - classpathEntryObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); + classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); } } // If classpath entry object is a URL-formatted string, convert to a URL instance - if (classpathEntryObj instanceof String) { - final String classpathEntryStr = (String) classpathEntryObj; + if (classpathEntObj instanceof String) { + final String classpathEntryStr = (String) classpathEntObj; if (JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntryStr).matches()) { try { - classpathEntryObj = new URL(classpathEntryStr); + classpathEntObj = new URL(classpathEntryStr); + } catch (final MalformedURLException e) { + try { + classpathEntObj = new URI(classpathEntryStr); + } catch (final URISyntaxException e1) { + throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); + } + } + } else if (classpathEntryStr.contains("!")) { + try { + classpathEntObj = new URL("jar:file:" + classpathEntryStr); } catch (final MalformedURLException e) { try { - classpathEntryObj = new URI(classpathEntryStr); + classpathEntObj = new URI(classpathEntryStr); } catch (final URISyntaxException e1) { throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); } @@ -417,8 +437,9 @@ public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpa // Check type of classpath entry object Path classpathEntryPath = null; - if (classpathEntryObj instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntryObj; + if (classpathEntObj instanceof URL) { + final URL classpathEntryURL = (URL) classpathEntObj; + // TODO scheme can be "jar" final String scheme = classpathEntryURL.getProtocol(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) @@ -431,8 +452,8 @@ public ClasspathElement newInstance(final ClasspathElementAndPackageRoot classpa @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementZip(classpathEntryURL, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(classpathEntryURL, + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e) { @@ -470,7 +491,7 @@ public ClasspathElement newInstance(final Object key, public ClasspathElement newInstance(final Object key, final LogNode log) { return new ClasspathElementZip(classpathEntryURL, - nestedJarHandler, scanSpec); + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -478,12 +499,13 @@ public ClasspathElement newInstance(final Object key, } } } - } else if (classpathEntryObj instanceof URI) { - final URI classpathEntryURI = (URI) classpathEntryObj; + } else if (classpathEntObj instanceof URI) { + final URI classpathEntryURI = (URI) classpathEntObj; final String scheme = classpathEntryURI.getScheme(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryURI, nestedJarHandler, scanSpec); + return new ClasspathElementZip(classpathEntryURI, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } else { try { // See if the URI resolves to a file or directory via the Path API @@ -516,7 +538,7 @@ public ClasspathElement newInstance(final Object key, public ClasspathElement newInstance(final Object key, final LogNode log) { return new ClasspathElementZip(classpathEntryURI, - nestedJarHandler, scanSpec); + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -524,8 +546,8 @@ public ClasspathElement newInstance(final Object key, } } } - } else if (classpathEntryObj instanceof Path) { - classpathEntryPath = (Path) classpathEntryObj; + } else if (classpathEntObj instanceof Path) { + classpathEntryPath = (Path) classpathEntObj; } else { // Fall through for any other object type (toString will be used to get path) } @@ -538,52 +560,51 @@ public ClasspathElement newInstance(final Object key, } catch (final SecurityException e) { // Ignore } - final Path packageRootPath = classpathEntryPath.resolve(dirOrPathPackageRoot); - if (FileUtils.canReadAndIsFile(packageRootPath)) { + if (FileUtils.canReadAndIsFile(classpathEntryPath)) { // packageRootPath is a Path which points to a lib/ext jar inside a parent Path try { - return classpathEntryObjToClasspathEntrySingletonMap.get(packageRootPath, log, + final Path path = classpathEntryPath; + return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, new NewInstanceFactory() { @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementZip(packageRootPath, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(path, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); } - } else if (FileUtils.canReadAndIsDir(packageRootPath)) { + } else if (FileUtils.canReadAndIsDir(classpathEntryPath)) { if ("JrtFileSystem" - .equals(packageRootPath.getFileSystem().getClass().getSimpleName())) { + .equals(classpathEntryPath.getFileSystem().getClass().getSimpleName())) { // Ignore JrtFileSystem (#553) -- paths are of form: // /modules/java.base/module-info.class - throw new IOException("Ignoring JrtFS filesystem path " + packageRootPath + throw new IOException("Ignoring JrtFS filesystem path " + classpathEntryPath + " (modules are scanned using the JPMS API)"); } // classpathEntryObj is a Path which points to a dir -- need to scan it recursively try { final Path path = classpathEntryPath; - final String root = dirOrPathPackageRoot; return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, new NewInstanceFactory() { @Override public ClasspathElement newInstance(final Object key, final LogNode log) { - return new ClasspathElementPathDir(path, root, nestedJarHandler, - scanSpec); + return new ClasspathElementPathDir(path, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + packageRootPath + " : " + e2); + throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); } } } // Fall through for other object types (including String) // Convert classpathEntryObj to a string - final String classpathEntryPathStr = classpathEntryObj.toString(); + final String classpathEntryPathStr = classpathEntObj.toString(); // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators final String pathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), @@ -622,7 +643,6 @@ public ClasspathElement newInstance(final Object key, } try { final boolean jar = isJar; - final String root = dirOrPathPackageRoot; return classpathEntryObjToClasspathEntrySingletonMap.get(pathCanonicalized, log, new NewInstanceFactory() { @Override @@ -630,10 +650,10 @@ public ClasspathElement newInstance(final Object key, final LogNode log) { // Instantiate a ClasspathElementZip or ClasspathElementDir singleton // for the classpath element path return jar - ? new ClasspathElementZip(pathCanonicalized, nestedJarHandler, - scanSpec) - : new ClasspathElementFileDir(fileCanonicalized, root, - nestedJarHandler, scanSpec); + ? new ClasspathElementZip(pathCanonicalized, classpathEntryWorkUnit, + nestedJarHandler, scanSpec) + : new ClasspathElementFileDir(fileCanonicalized, + classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -644,7 +664,7 @@ public ClasspathElement newInstance(final Object key, final LogNode log) { /** * Create a WorkUnitProcessor for opening traditional classpath entries (which are mapped to - * {@link ClasspathElementFileDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled + * {@link ClasspathElementPathDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * * @param allClasspathElts @@ -666,21 +686,14 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // Create a ClasspathElementZip or ClasspathElementDir for each entry in the classpath ClasspathElement classpathElt; try { - classpathElt = classpathEntryToClasspathElementSingletonMap.get(workUnit.rawClasspathEntry, - log); - // Set index within parent, if it hasn't already been set - classpathElt.classpathElementIdxWithinParent.compareAndSet(-1, - workUnit.classpathElementIdxWithinParent); - // Set classloader, if it hasn't already been set - classpathElt.classLoader.compareAndSet(null, workUnit.rawClasspathEntry.classLoader); + // Get or create ClasspathElement from raw classpath entry object + classpathElt = classpathEntryWorkUnitToClasspathElementSingletonMap.get(workUnit, log); } catch (final NullSingletonException | NewInstanceException e) { throw new IOException("Cannot get classpath element for classpath entry " - + workUnit.rawClasspathEntry + " : " + (e.getCause() == null ? e : e.getCause())); + + workUnit.classpathEntryObj + " : " + (e.getCause() == null ? e : e.getCause())); } - // Only run open() once per ClasspathElement (it is possible for there to be - // multiple classpath elements with different non-canonical paths that map to - // the same canonical path, i.e. to the same ClasspathElement) + // Only run open() once per ClasspathElement if (allClasspathElts.add(classpathElt)) { final LogNode subLog = log == null ? null : log.log("Opening classpath element " + classpathElt); @@ -701,11 +714,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, } } catch (final IOException | SecurityException e) { if (log != null) { - log.log("Skipping invalid classpath element " - + workUnit.rawClasspathEntry.classpathElementObj - + (workUnit.rawClasspathEntry.dirOrPathPackageRoot.isEmpty() ? "" - : "/" + workUnit.rawClasspathEntry.dirOrPathPackageRoot) - + " : " + e); + log.log("Skipping invalid classpath element " + workUnit.classpathEntryObj + " : " + e); } } } @@ -931,9 +940,12 @@ private void preprocessClasspathElementsByType(final List fina final List> classpathEltDirs = new ArrayList<>(); final List> classpathEltZips = new ArrayList<>(); for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { - if (classpathElt instanceof ClasspathElementFileDir) { - // Separate out ClasspathElementDir elements from other types - classpathEltDirs.add(new SimpleEntry<>(classpathElt.getFile().getPath(), classpathElt)); + if (classpathElt instanceof ClasspathElementFileDir + || classpathElt instanceof ClasspathElementPathDir) { + // Separate out ClasspathElementFileDir and ClasspathElementPathDir elements from other types + final File file = classpathElt.getFile(); + final String path = file == null ? classpathElt.toString() : file.getPath(); + classpathEltDirs.add(new SimpleEntry<>(path, classpathElt)); } else if (classpathElt instanceof ClasspathElementZip) { // Separate out ClasspathElementZip elements from other types @@ -1128,14 +1140,14 @@ private ScanResult performScan(final List finalClasspathEltOrd private ScanResult openClasspathElementsThenScan() throws InterruptedException, ExecutionException { // Get order of elements in traditional classpath final List rawClasspathEntryWorkUnits = new ArrayList<>(); - final List rawClasspathOrder = classpathFinder.getClasspathOrder() - .getOrder(); - for (final ClasspathElementAndPackageRoot rawClasspathEntry : rawClasspathOrder) { - rawClasspathEntryWorkUnits - .add(new ClasspathEntryWorkUnit(rawClasspathEntry, /* parentClasspathElement = */ null, - // classpathElementIdxWithinParent is the original classpath index, - // for toplevel classpath elements - /* classpathElementIdxWithinParent = */ rawClasspathEntryWorkUnits.size())); + final List rawClasspathOrder = classpathFinder.getClasspathOrder().getOrder(); + for (final ClasspathEntry rawClasspathEntry : rawClasspathOrder) { + rawClasspathEntryWorkUnits.add(new ClasspathEntryWorkUnit(rawClasspathEntry.classpathEntryObj, + rawClasspathEntry.classLoader, /* parentClasspathElement = */ null, + // classpathElementIdxWithinParent is the original classpath index, + // for toplevel classpath elements + /* classpathElementIdxWithinParent = */ rawClasspathEntryWorkUnits.size(), + /* packageRootPrefix = */ "")); } // In parallel, create a ClasspathElement singleton for each classpath element, then call open() diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index e43fad158..ecbe58e11 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -62,7 +62,7 @@ public class ClasspathOrder { private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); /** The classpath order. Keys are instances of {@link String} or {@link URL}. */ - private final List order = new ArrayList<>(); + private final List order = new ArrayList<>(); /** Suffixes for automatic package roots, e.g. "!/BOOT-INF/classes". */ private static final List AUTOMATIC_PACKAGE_ROOT_SUFFIXES = new ArrayList<>(); @@ -76,71 +76,45 @@ public class ClasspathOrder { /** * A classpath element and the {@link ClassLoader} it was obtained from. */ - public static class ClasspathElementAndPackageRoot { - /** - * The classpath element root (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). - */ - public final Object classpathElementObj; - - /** The classpath element package root, prefix, e.g. "BOOT-INF/classes" or "". */ - public final String dirOrPathPackageRoot; + public static class ClasspathEntry { + /** The classpath entry object (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ + public final Object classpathEntryObj; /** The classloader the classpath element was obtained from. */ public final ClassLoader classLoader; - /** - * Constructor for directory or {@link Path} classpath entries. - * - * @param classpathElementObj - * the classpath element object (a {@link String} path, {@link Path}, {@link URL} or - * {@link URI}). - * @param dirOrPathPackageRoot - * the classpath element package root prefix, e.g. "BOOT-INF/classes" or "". Only used for - * directory or {@link Path} classpath entries. - * @param classLoader - * the classloader the classpath element was obtained from. - */ - public ClasspathElementAndPackageRoot(final Object classpathElementObj, final String dirOrPathPackageRoot, - final ClassLoader classLoader) { - this.classpathElementObj = classpathElementObj; - this.dirOrPathPackageRoot = dirOrPathPackageRoot; - this.classLoader = classLoader; - } - /** * Constructor. * - * @param classpathElementRoot - * the classpath element root (a {@link String} or {@link URL} or {@link Path}). + * @param classpathEntryObj + * the classpath entry object (a {@link String} or {@link URL} or {@link Path}). * @param classLoader * the classloader the classpath element was obtained from. */ - public ClasspathElementAndPackageRoot(final Object classpathElementRoot, final ClassLoader classLoader) { - this.classpathElementObj = classpathElementRoot; - this.dirOrPathPackageRoot = ""; + public ClasspathEntry(final Object classpathEntryObj, final ClassLoader classLoader) { + this.classpathEntryObj = classpathEntryObj; this.classLoader = classLoader; } @Override public int hashCode() { - return Objects.hash(classpathElementObj, dirOrPathPackageRoot, classLoader); + return Objects.hash(classpathEntryObj); } @Override public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ClasspathElementAndPackageRoot)) { + } else if (!(obj instanceof ClasspathEntry)) { return false; } - final ClasspathElementAndPackageRoot other = (ClasspathElementAndPackageRoot) obj; - return Objects.equals(this.dirOrPathPackageRoot, other.dirOrPathPackageRoot) - && Objects.equals(this.classpathElementObj, other.classpathElementObj); + final ClasspathEntry other = (ClasspathEntry) obj; + return Objects.equals(this.classpathEntryObj, other.classpathEntryObj); } @Override public String toString() { - return classpathElementObj + " [" + classLoader + "]"; + return classpathEntryObj + " [" + classLoader + "]"; } } @@ -159,7 +133,7 @@ public String toString() { * * @return the classpath order. */ - public List getOrder() { + public List getOrder() { return order; } @@ -208,7 +182,7 @@ private boolean filter(final URL classpathElementURL, final String classpathElem */ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classLoader) { if (classpathEntryUniqueResolvedPaths.add(pathEntry)) { - order.add(new ClasspathElementAndPackageRoot(pathEntry, classLoader)); + order.add(new ClasspathEntry(pathEntry, classLoader)); return true; } return false; @@ -260,7 +234,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle // Deduplicate classpath elements if (classpathEntryUniqueResolvedPaths.add(pathElementStrWithoutSuffix)) { // Record classpath element in classpath order - order.add(new ClasspathElementAndPackageRoot(pathElementWithoutSuffix, classLoader)); + order.add(new ClasspathEntry(pathElementWithoutSuffix, classLoader)); return true; } } else { @@ -274,7 +248,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle return false; } if (classpathEntryUniqueResolvedPaths.add(pathElementStrResolved)) { - order.add(new ClasspathElementAndPackageRoot(pathElementStrResolved, classLoader)); + order.add(new ClasspathEntry(pathElementStrResolved, classLoader)); return true; } } diff --git a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java index a76cb62fa..d62d33735 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -30,6 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.MalformedURLException; +import java.net.URL; + import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -47,8 +50,19 @@ public void issue46Test() { final String jarPath = "jar:file://" + Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; - try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().verbose() + .scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } +// +// public static void main(String[] args) throws MalformedURLException { +// final String jarPath = "jar:file:" + +// Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() +// + "!/level2.jar!/level3.jar!/classpath1/classpath2"; +// System.out.println(jarPath); +// URL u = new URL(jarPath); +// System.out.println(u); +// System.out.println(u.getPath()); +// } } From ca61ef48e46e7808f77153eb706ccfe6443820b6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 23:00:05 -0600 Subject: [PATCH 391/713] Final fixes for #673 --- .../classgraph/ClasspathElementFileDir.java | 2 + .../java/io/github/classgraph/Scanner.java | 91 +++++++++++-------- .../classgraph/concurrency/SingletonMap.java | 14 +-- .../issues/issue46/Issue46Test.java | 16 +--- 4 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index c9dc35e20..8594d86c5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -60,6 +60,8 @@ /** A directory classpath element, using the {@link File} API. */ class ClasspathElementFileDir extends ClasspathElement { + // TODO: this class is no longer used, in favor of ClasspathElementPathDir + /** The directory at the root of the classpath element. */ private final File classpathEltDir; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 4520dc95c..fd3c310af 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -375,7 +375,7 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader classpathEntryObjToClasspathEntrySingletonMap = // new SingletonMap() { @Override - public ClasspathElement newInstance(final Object classpathEntryObj, final LogNode log) + public ClasspathElement newInstance(final Object key, final LogNode log) throws IOException, InterruptedException { // Each use of this map provides a NewInstanceFactory throw new IOException("This method should not be called"); @@ -398,42 +398,58 @@ public ClasspathElement newInstance(final ClasspathEntryWorkUnit classpathEntryW throw new IOException("Got null classpath entry object"); } + // Convert URL/URI into String, to handle some normalization. // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end + // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end. + // Also strip any "jar:file:" or "file:" off the beginning. + // This normalizes "file:x.jar" and "x.jar" to the same string, for example. if (classpathEntObj instanceof URL || classpathEntObj instanceof URI) { - final String classpathEntryStr = classpathEntObj.toString(); - if (classpathEntryStr.endsWith("!/")) { - classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 2); - } else if (classpathEntryStr.endsWith("!")) { - classpathEntObj = classpathEntryStr.substring(0, classpathEntryStr.length() - 1); - } + classpathEntObj = FastPathResolver.resolve(classpathEntObj.toString()); } - // If classpath entry object is a URL-formatted string, convert to a URL instance + // If classpath entry object is a URL-formatted string, convert to (or back to) a URL instance. if (classpathEntObj instanceof String) { - final String classpathEntryStr = (String) classpathEntObj; - if (JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntryStr).matches()) { + String classpathEntStr = (String) classpathEntObj; + final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntStr).matches(); + final boolean isMultiSection = classpathEntStr.contains("!"); + if (isURL || isMultiSection) { + // Convert back to URL (or URI) if this has a URL scheme or if this is a multi-section + // path (which needs the "jar:file:" scheme) + if (!isURL) { + // Add "file:" scheme if there is no scheme + classpathEntStr = "file:" + classpathEntStr; + } + if (isMultiSection) { + // Multi-section URL strings that do not already have a URL scheme need to + // have the "jar:file:" scheme + classpathEntStr = "jar:" + classpathEntStr; + // Also "jar:" URLs need at least one instance of "!/" -- if only "!" is used + // without a subsequent "/", replace it + classpathEntStr = classpathEntStr.replaceAll("!([^/])", "!/$1"); + } try { - classpathEntObj = new URL(classpathEntryStr); + // Convert classpath entry to (or back to) a URL. + classpathEntObj = new URL(classpathEntStr); } catch (final MalformedURLException e) { + // Try creating URI if URL creation fails, in case there is a URI-only scheme try { - classpathEntObj = new URI(classpathEntryStr); + classpathEntObj = new URI(classpathEntStr); } catch (final URISyntaxException e1) { - throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); + throw new IOException("Malformed URI: " + classpathEntObj + " : " + e1); } } - } else if (classpathEntryStr.contains("!")) { + } else { + // If this is not a URL with a non-"file:" scheme and is not a multi-section URL, + // try parsing the path string as a Path try { - classpathEntObj = new URL("jar:file:" + classpathEntryStr); - } catch (final MalformedURLException e) { - try { - classpathEntObj = new URI(classpathEntryStr); - } catch (final URISyntaxException e1) { - throw new IOException("Malformed URI: " + classpathEntryStr + " : " + e1); - } + classpathEntObj = Paths.get(classpathEntStr); + } catch (final InvalidPathException e) { + throw new IOException("Malformed path: " + classpathEntObj + " : " + e); } } } + // At this point, String is dealt with and String, URL, and URI classpath elements are all + // normalized together. classpathEntObj is either a URL, URI, or Path. // Check type of classpath entry object Path classpathEntryPath = null; @@ -448,10 +464,9 @@ public ClasspathElement newInstance(final ClasspathEntryWorkUnit classpathEntryW // Use toString() for the key so that URLs and URIs that resolve to the // same resource will point to the same entry in the singleton map classpathEntryURL.toString(), log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, - final LogNode log) { + public ClasspathElement newInstance() { return new ClasspathElementZip(classpathEntryURL, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } @@ -486,10 +501,9 @@ public ClasspathElement newInstance(final Object key, // Use toString() for the key so that URLs and URIs that resolve to the // same resource will point to the same entry in the singleton map classpathEntryURL.toString(), log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, - final LogNode log) { + public ClasspathElement newInstance() { return new ClasspathElementZip(classpathEntryURL, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } @@ -533,10 +547,9 @@ public ClasspathElement newInstance(final Object key, // Use toString() for the key so that URLs and URIs that resolve to the // same resource will point to the same entry in the singleton map classpathEntryURI.toString(), log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, - final LogNode log) { + public ClasspathElement newInstance() { return new ClasspathElementZip(classpathEntryURI, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } @@ -565,10 +578,9 @@ public ClasspathElement newInstance(final Object key, try { final Path path = classpathEntryPath; return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, - final LogNode log) { + public ClasspathElement newInstance() { return new ClasspathElementZip(path, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } @@ -588,10 +600,9 @@ public ClasspathElement newInstance(final Object key, try { final Path path = classpathEntryPath; return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, - final LogNode log) { + public ClasspathElement newInstance() { return new ClasspathElementPathDir(path, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } @@ -644,15 +655,15 @@ public ClasspathElement newInstance(final Object key, try { final boolean jar = isJar; return classpathEntryObjToClasspathEntrySingletonMap.get(pathCanonicalized, log, - new NewInstanceFactory() { + new NewInstanceFactory() { @Override - public ClasspathElement newInstance(final Object key, final LogNode log) { + public ClasspathElement newInstance() { // Instantiate a ClasspathElementZip or ClasspathElementDir singleton // for the classpath element path return jar ? new ClasspathElementZip(pathCanonicalized, classpathEntryWorkUnit, nestedJarHandler, scanSpec) - : new ClasspathElementFileDir(fileCanonicalized, + : new ClasspathElementPathDir(pathCanonicalized, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java index c7b5931fc..1ab4b7844 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -166,23 +166,17 @@ V get() throws InterruptedException { /** * Create a new instance. * - * @param - * The key type. * @param * The instance type. */ @FunctionalInterface - public interface NewInstanceFactory { + public interface NewInstanceFactory { /** * Create a new instance. * - * @param key - * The key. - * @param log - * The log. * @return The new instance. */ - public V newInstance(K key, LogNode log); + public V newInstance(); } /** @@ -215,7 +209,7 @@ public interface NewInstanceFactory { * @throws NewInstanceException * if {@link #newInstance(Object, LogNode)} threw an exception. */ - public V get(final K key, final LogNode log, final NewInstanceFactory newInstanceFactory) + public V get(final K key, final LogNode log, final NewInstanceFactory newInstanceFactory) throws E, InterruptedException, NullSingletonException, NewInstanceException { final SingletonHolder singletonHolder = map.get(key); @SuppressWarnings("null") @@ -237,7 +231,7 @@ public V get(final K key, final LogNode log, final NewInstanceFactory newI // Create a new instance if (newInstanceFactory != null) { // Call NewInstanceFactory - instance = newInstanceFactory.newInstance(key, log); + instance = newInstanceFactory.newInstance(); } else { // Call overridden newInstance method instance = newInstance(key, log); diff --git a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java index d62d33735..a76cb62fa 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -30,9 +30,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.net.MalformedURLException; -import java.net.URL; - import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -50,19 +47,8 @@ public void issue46Test() { final String jarPath = "jar:file://" + Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; - try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().verbose() - .scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } -// -// public static void main(String[] args) throws MalformedURLException { -// final String jarPath = "jar:file:" + -// Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() -// + "!/level2.jar!/level3.jar!/classpath1/classpath2"; -// System.out.println(jarPath); -// URL u = new URL(jarPath); -// System.out.println(u); -// System.out.println(u.getPath()); -// } } From 2c27b87122138ecb585eb85e1b854b95b382c703 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 14 Apr 2022 23:01:23 -0600 Subject: [PATCH 392/713] Remove ClasspathElementFileDir class --- ...tPathDir.java => ClasspathElementDir.java} | 8 +- .../classgraph/ClasspathElementFileDir.java | 556 ------------------ .../java/io/github/classgraph/Scanner.java | 11 +- 3 files changed, 9 insertions(+), 566 deletions(-) rename src/main/java/io/github/classgraph/{ClasspathElementPathDir.java => ClasspathElementDir.java} (98%) delete mode 100644 src/main/java/io/github/classgraph/ClasspathElementFileDir.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java similarity index 98% rename from src/main/java/io/github/classgraph/ClasspathElementPathDir.java rename to src/main/java/io/github/classgraph/ClasspathElementDir.java index 73dc33678..d2f421220 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -62,7 +62,7 @@ import nonapi.io.github.classgraph.utils.VersionFinder; /** A directory classpath element, using the {@link Path} API. */ -class ClasspathElementPathDir extends ClasspathElement { +class ClasspathElementDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final Path classpathEltPath; @@ -84,7 +84,7 @@ class ClasspathElementPathDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementPathDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, + ClasspathElementDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(workUnit, scanSpec); this.classpathEltPath = classpathEltPath; @@ -594,10 +594,10 @@ public int hashCode() { public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ClasspathElementPathDir)) { + } else if (!(obj instanceof ClasspathElementDir)) { return false; } - final ClasspathElementPathDir other = (ClasspathElementPathDir) obj; + final ClasspathElementDir other = (ClasspathElementDir) obj; return Objects.equals(this.classpathEltPath, other.classpathEltPath); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java deleted file mode 100644 index 8594d86c5..000000000 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Luke Hutchison - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO - * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.github.classgraph; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.attribute.PosixFileAttributes; -import java.nio.file.attribute.PosixFilePermission; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; -import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; -import nonapi.io.github.classgraph.concurrency.WorkQueue; -import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; -import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; -import nonapi.io.github.classgraph.fileslice.FileSlice; -import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; -import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; -import nonapi.io.github.classgraph.utils.FastPathResolver; -import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.LogNode; -import nonapi.io.github.classgraph.utils.VersionFinder; - -/** A directory classpath element, using the {@link File} API. */ -class ClasspathElementFileDir extends ClasspathElement { - // TODO: this class is no longer used, in favor of ClasspathElementPathDir - - /** The directory at the root of the classpath element. */ - private final File classpathEltDir; - - /** Used to ensure that recursive scanning doesn't get into an infinite loop due to a link cycle. */ - private final Set scannedCanonicalPaths = new HashSet<>(); - - /** The nested jar handler. */ - private final NestedJarHandler nestedJarHandler; - - /** - * A directory classpath element. - * - * @param classpathEltDir - * the classpath element directory - * @param workUnit - * the work unit - * @param nestedJarHandler - * the nested jar handler - * @param scanSpec - * the scan spec - */ - ClasspathElementFileDir(final File classpathEltDir, final ClasspathEntryWorkUnit workUnit, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { - super(workUnit, scanSpec); - this.classpathEltDir = classpathEltDir; - this.nestedJarHandler = nestedJarHandler; - } - - /* (non-Javadoc) - * @see io.github.classgraph.ClasspathElement#open( - * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) - */ - @Override - void open(final WorkQueue workQueue, final LogNode log) { - if (!scanSpec.scanDirs) { - if (log != null) { - log(classpathElementIdx, - "Skipping classpath element, since dir scanning is disabled: " + classpathEltDir, log); - } - skipClasspathElement = true; - return; - } - try { - // Auto-add nested lib dirs - int childClasspathEntryIdx = 0; - for (final String libDirPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES) { - final File libDir = new File(classpathEltDir, libDirPrefix); - if (FileUtils.canReadAndIsDir(libDir)) { - // Sort directory entries for consistency - final File[] listFiles = libDir.listFiles(); - if (listFiles != null) { - Arrays.sort(listFiles); - // Add all jarfiles within lib dir as child classpath entries - for (final File file : listFiles) { - if (file.isFile() && file.getName().endsWith(".jar")) { - if (log != null) { - log(classpathElementIdx, "Found lib jar: " + file, log); - } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit(file.getPath(), getClassLoader(), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, - /* packageRootPrefix = */ "")); - } - } - } - } - } - // Only look for package roots if the package root is empty - if (packageRootPrefix.isEmpty()) { - for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { - final File packageRoot = new File(classpathEltDir, packageRootPrefix); - if (FileUtils.canReadAndIsDir(packageRoot)) { - if (log != null) { - log(classpathElementIdx, "Found package root: " + packageRoot, log); - } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit(packageRoot, getClassLoader(), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, - packageRootPrefix)); - } - } - } - } catch (final SecurityException e) { - if (log != null) { - log(classpathElementIdx, - "Skipping classpath element, since dir cannot be accessed: " + classpathEltDir, log); - } - skipClasspathElement = true; - } - } - - /** - * Create a new {@link Resource} object for a resource or classfile discovered while scanning paths. - * - * @param pathRelativeToPackageRoot - * the path of the resource relative to the package root - * @param resourceFile - * the {@link File} for the resource - * @param nestedJarHandler - * the nested jar handler - * @return the resource - */ - private Resource newResource(final String pathRelativeToPackageRoot, final File resourceFile, - final NestedJarHandler nestedJarHandler) { - return new Resource(this, resourceFile.length()) { - /** The {@link FileSlice} opened on the file. */ - private FileSlice fileSlice; - - /** True if the resource is open. */ - private final AtomicBoolean isOpen = new AtomicBoolean(); - - @Override - public String getPath() { - String path = FastPathResolver.resolve(pathRelativeToPackageRoot); - while (path.startsWith("/")) { - path = path.substring(1); - } - return path; - } - - @Override - public String getPathRelativeToClasspathElement() { - return packageRootPrefix.isEmpty() ? getPath() : packageRootPrefix + getPath(); - } - - @Override - public long getLastModified() { - return resourceFile.lastModified(); - } - - @SuppressWarnings("null") - @Override - public Set getPosixFilePermissions() { - Set posixFilePermissions = null; - try { - posixFilePermissions = Files.readAttributes(resourceFile.toPath(), PosixFileAttributes.class) - .permissions(); - } catch (UnsupportedOperationException | IOException | SecurityException e) { - // POSIX attributes not supported - } - return posixFilePermissions; - } - - @Override - public ByteBuffer read() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } - fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - length = fileSlice.sliceLength; - byteBuffer = fileSlice.read(); - return byteBuffer; - } - - @Override - ClassfileReader openClassfile() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } - // Classfile won't be compressed, so wrap it in a new FileSlice and then open it - fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - length = fileSlice.sliceLength; - return new ClassfileReader(fileSlice, this); - } - - @Override - public InputStream open() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } - fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - inputStream = fileSlice.open(this); - length = fileSlice.sliceLength; - return inputStream; - } - - @Override - public byte[] load() throws IOException { - read(); - try (Resource res = this) { // Close this after use - fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); - final byte[] bytes = fileSlice.load(); - length = bytes.length; - return bytes; - } - } - - @Override - public void close() { - if (isOpen.getAndSet(false)) { - if (byteBuffer != null) { - // Any ByteBuffer ref should be a duplicate, so it doesn't need to be cleaned - byteBuffer = null; - } - if (fileSlice != null) { - fileSlice.close(); - nestedJarHandler.markSliceAsClosed(fileSlice); - fileSlice = null; - } - - // Close inputStream - super.close(); - } - } - }; - } - - /** - * Get the {@link Resource} for a given relative path. - * - * @param pathRelativeToPackageRoot - * The relative path of the {@link Resource} to return. - * @return The {@link Resource} for the given relative path, or null if relativePath does not exist in this - * classpath element. - */ - @Override - Resource getResource(final String pathRelativeToPackageRoot) { - final File resourceFile = new File(classpathEltDir, pathRelativeToPackageRoot); - return FileUtils.canReadAndIsFile(resourceFile) - ? newResource(pathRelativeToPackageRoot, resourceFile, nestedJarHandler) - : null; - } - - /** - * Recursively scan a directory for file path patterns matching the scan spec. - * - * @param dir - * the directory - * @param log - * the log - */ - private void scanDirRecursively(final File dir, final LogNode log) { - if (skipClasspathElement) { - return; - } - // See if this canonical path has been scanned before, so that recursive scanning doesn't get stuck in an - // infinite loop due to symlinks - String canonicalPath; - try { - canonicalPath = dir.getCanonicalPath(); - if (!scannedCanonicalPaths.add(canonicalPath)) { - if (log != null) { - log.log("Reached symlink cycle, stopping recursion: " + dir); - } - return; - } - } catch (final IOException | SecurityException e) { - if (log != null) { - log.log("Could not canonicalize path: " + dir, e); - } - return; - } - - final String dirPath = dir.getPath(); - final int ignorePrefixLen = classpathEltDir.getPath().length() + 1; - final String dirRelativePath = ignorePrefixLen > dirPath.length() ? "/" // - : dirPath.substring(ignorePrefixLen).replace(File.separatorChar, '/') + "/"; - final boolean isDefaultPackage = "/".equals(dirRelativePath); - - if (nestedClasspathRootPrefixes != null && nestedClasspathRootPrefixes.contains(dirRelativePath)) { - if (log != null) { - log.log("Reached nested classpath root, stopping recursion to avoid duplicate scanning: " - + dirRelativePath); - } - return; - } - - // Ignore versioned sections in exploded jars -- they are only supposed to be used in jars. - // TODO: is it necessary to support multi-versioned exploded jars anyway? If so, all the paths in a - // directory classpath entry will have to be pre-scanned and masked, as happens in ClasspathElementZip. - if (dirRelativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { - if (log != null) { - log.log("Found unexpected nested versioned entry in directory classpath element -- skipping: " - + dirRelativePath); - } - return; - } - - // Accept/reject classpath elements based on dir resource paths - checkResourcePathAcceptReject(dirRelativePath, log); - if (skipClasspathElement) { - return; - } - - final ScanSpecPathMatch parentMatchStatus = scanSpec.dirAcceptMatchStatus(dirRelativePath); - if (parentMatchStatus == ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX) { - // Reached a non-accepted or rejected path -- stop the recursive scan - if (log != null) { - log.log("Reached rejected directory, stopping recursive scan: " + dirRelativePath); - } - return; - } - if (parentMatchStatus == ScanSpecPathMatch.NOT_WITHIN_ACCEPTED_PATH) { - // Reached a non-accepted and non-rejected path -- stop the recursive scan - return; - } - - final LogNode subLog = log == null ? null - // Log dirs after files (addAcceptedResources() precedes log entry with "0:") - : log.log("1:" + canonicalPath, "Scanning directory: " + dir - + (dir.getPath().equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); - - final File[] filesInDir = dir.listFiles(); - if (filesInDir == null) { - if (log != null) { - log.log("Invalid directory " + dir); - } - return; - } - Arrays.sort(filesInDir); - - // Determine whether this is a modular jar running under JRE 9+ - final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; - - // Only scan files in directory if directory is not only an ancestor of an accepted path - if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH) { - // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses - for (final File fileInDir : filesInDir) { - // Process files in dir before recursing - if (fileInDir.isFile()) { - final String fileInDirRelativePath = dirRelativePath.isEmpty() || isDefaultPackage - ? fileInDir.getName() - : dirRelativePath + fileInDir.getName(); - // If this is a modular jar, ignore all classfiles other than "module-info.class" in the - // default package, since these are disallowed. - if (isModularJar && isDefaultPackage && fileInDirRelativePath.endsWith(".class") - && !fileInDirRelativePath.equals("module-info.class")) { - continue; - } - - // Accept/reject classpath elements based on file resource paths - checkResourcePathAcceptReject(fileInDirRelativePath, subLog); - if (skipClasspathElement) { - return; - } - - // If relative path is accepted - if (parentMatchStatus == ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyAccepted(fileInDirRelativePath))) { - // Resource is accepted - final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler); - addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); - - // Save last modified time - fileToLastModified.put(fileInDir, fileInDir.lastModified()); - } else { - if (subLog != null) { - subLog.log("Skipping non-accepted file: " + fileInDirRelativePath); - } - } - } - } - } else if (scanSpec.enableClassInfo && dirRelativePath.equals("/")) { - // Always check for module descriptor in package root, even if package root isn't in accept - for (final File fileInDir : filesInDir) { - if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { - final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler); - addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); - fileToLastModified.put(fileInDir, fileInDir.lastModified()); - break; - } - } - } - // Recurse into subdirectories - for (final File fileInDir : filesInDir) { - if (fileInDir.isDirectory()) { - scanDirRecursively(fileInDir, subLog); - // If a rejected classpath element resource path was found, it will set skipClasspathElement - if (skipClasspathElement) { - if (subLog != null) { - subLog.addElapsedTime(); - } - return; - } - } - } - - if (subLog != null) { - subLog.addElapsedTime(); - } - - // Save the last modified time of the directory - fileToLastModified.put(dir, dir.lastModified()); - } - - /** - * Hierarchically scan directory structure for classfiles and matching files. - * - * @param log - * the log - */ - @Override - void scanPaths(final LogNode log) { - if (skipClasspathElement) { - return; - } - if (scanned.getAndSet(true)) { - // Should not happen - throw new IllegalArgumentException("Already scanned classpath element " + this); - } - - final LogNode subLog = log == null ? null - : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltDir, log); - - scanDirRecursively(classpathEltDir, subLog); - - finishScanPaths(subLog); - } - - /** - * Get the module name from module descriptor. - * - * @return the module name - */ - @Override - public String getModuleName() { - return moduleNameFromModuleDescriptor == null || moduleNameFromModuleDescriptor.isEmpty() ? null - : moduleNameFromModuleDescriptor; - } - - /** - * Get the directory {@link File}. - * - * @return The classpath element directory as a {@link File}. - */ - @Override - public File getFile() { - return classpathEltDir; - } - - /* (non-Javadoc) - * @see io.github.classgraph.ClasspathElement#getURI() - */ - @Override - URI getURI() { - return classpathEltDir.toURI(); - } - - @Override - List getAllURIs() { - return Collections.singletonList(getURI()); - } - - /** - * Return the classpath element directory as a String. - * - * @return the string - */ - @Override - public String toString() { - return classpathEltDir.toString(); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return Objects.hash(classpathEltDir); - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } else if (!(obj instanceof ClasspathElementFileDir)) { - return false; - } - final ClasspathElementFileDir other = (ClasspathElementFileDir) obj; - return Objects.equals(this.classpathEltDir, other.classpathEltDir); - } -} diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index fd3c310af..f146daf7d 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -603,7 +603,7 @@ public ClasspathElement newInstance() { new NewInstanceFactory() { @Override public ClasspathElement newInstance() { - return new ClasspathElementPathDir(path, classpathEntryWorkUnit, + return new ClasspathElementDir(path, classpathEntryWorkUnit, nestedJarHandler, scanSpec); } }); @@ -663,8 +663,8 @@ public ClasspathElement newInstance() { return jar ? new ClasspathElementZip(pathCanonicalized, classpathEntryWorkUnit, nestedJarHandler, scanSpec) - : new ClasspathElementPathDir(pathCanonicalized, - classpathEntryWorkUnit, nestedJarHandler, scanSpec); + : new ClasspathElementDir(pathCanonicalized, classpathEntryWorkUnit, + nestedJarHandler, scanSpec); } }); } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { @@ -675,7 +675,7 @@ public ClasspathElement newInstance() { /** * Create a WorkUnitProcessor for opening traditional classpath entries (which are mapped to - * {@link ClasspathElementPathDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled + * {@link ClasspathElementDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * * @param allClasspathElts @@ -951,8 +951,7 @@ private void preprocessClasspathElementsByType(final List fina final List> classpathEltDirs = new ArrayList<>(); final List> classpathEltZips = new ArrayList<>(); for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { - if (classpathElt instanceof ClasspathElementFileDir - || classpathElt instanceof ClasspathElementPathDir) { + if (classpathElt instanceof ClasspathElementDir) { // Separate out ClasspathElementFileDir and ClasspathElementPathDir elements from other types final File file = classpathElt.getFile(); final String path = file == null ? classpathElt.toString() : file.getPath(); From c543b9c11120e0479bc4eeccefc113dd0b435d26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 00:51:13 -0600 Subject: [PATCH 393/713] Reduce chance of duplicate opening of cp elts (#673) --- .../java/io/github/classgraph/Scanner.java | 544 +++++++----------- .../classgraph/concurrency/SingletonMap.java | 6 +- .../io/github/classgraph/utils/FileUtils.java | 24 +- .../issues/issue673/Issue673Test.java | 6 + 4 files changed, 246 insertions(+), 334 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index f146daf7d..2af3f1215 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -29,7 +29,6 @@ package io.github.classgraph; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; @@ -67,8 +66,7 @@ import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; -import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceException; -import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NewInstanceFactory; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.concurrency.WorkQueue.WorkUnitProcessor; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -327,7 +325,7 @@ private void processWorkUnits(final Collection workUnits, final LogNode l /** Used to enqueue classpath elements for opening. */ static class ClasspathEntryWorkUnit { /** The classpath entry object (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). */ - final Object classpathEntryObj; + Object classpathEntryObj; /** The classloader the classpath entry object was obtained from. */ final ClassLoader classLoader; @@ -366,6 +364,143 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader } } + // ------------------------------------------------------------------------------------------------------------- + + private static Object normalizeClasspathEntry(Object classpathEntryObj) throws IOException { + if (classpathEntryObj == null) { + // Should not happen + throw new IOException("Got null classpath entry object"); + } + + // Convert URL/URI (or anything other than URL/URI, or Path) into a String. + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end. + // Also strip any "jar:file:" or "file:" off the beginning. + // This normalizes "file:x.jar" and "x.jar" to the same string, for example. + if (!(classpathEntryObj instanceof Path)) { + classpathEntryObj = FastPathResolver.resolve(FileUtils.currDirPath(), classpathEntryObj.toString()); + } + + // If classpath entry object is a URL-formatted string, convert to (or back to) a URL instance. + if (classpathEntryObj instanceof String) { + String classpathEntStr = (String) classpathEntryObj; + final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntStr).matches(); + final boolean isMultiSection = classpathEntStr.contains("!"); + if (isURL || isMultiSection) { + // Convert back to URL (or URI) if this has a URL scheme or if this is a multi-section + // path (which needs the "jar:file:" scheme) + if (!isURL) { + // Add "file:" scheme if there is no scheme + classpathEntStr = "file:" + classpathEntStr; + } + if (isMultiSection) { + // Multi-section URL strings that do not already have a URL scheme need to + // have the "jar:file:" scheme + classpathEntStr = "jar:" + classpathEntStr; + // Also "jar:" URLs need at least one instance of "!/" -- if only "!" is used + // without a subsequent "/", replace it + classpathEntStr = classpathEntStr.replaceAll("!([^/])", "!/$1"); + } + try { + // Convert classpath entry to (or back to) a URL. + classpathEntryObj = new URL(classpathEntStr); + } catch (final MalformedURLException e) { + // Try creating URI if URL creation fails, in case there is a URI-only scheme + try { + classpathEntryObj = new URI(classpathEntStr); + } catch (final URISyntaxException e1) { + throw new IOException("Malformed URI: " + classpathEntryObj + " : " + e1); + } + } + } + // Last-ditch effort -- try to convert String to Path + if (classpathEntryObj instanceof String) { + try { + classpathEntryObj = Paths.get((String) classpathEntryObj); + } catch (final InvalidPathException e) { + throw new IOException("Malformed path: " + classpathEntryObj + " : " + e); + } + } + } + // At this point, String is dealt with and String, URL, and URI classpath elements are all + // normalized together. classpathEntObj is either a URL, URI, or Path. + + // Check type of classpath entry object + if (classpathEntryObj instanceof URL) { + final URL classpathEntryURL = (URL) classpathEntryObj; + final String scheme = classpathEntryURL.getProtocol(); + if (!"http".equals(scheme) && !"https".equals(scheme)) { + try { + // See if the URL resolves to a file or directory via the Path API + classpathEntryObj = Paths.get(classpathEntryURL.toURI()); + } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) + try { + classpathEntryObj = new File(classpathEntryURL.toURI()).toPath(); + } catch (IllegalArgumentException | URISyntaxException e2) { + // Try normalizing path (which would reduce this to simply "myjar.jar") + // and then passing it again to Paths.get. + try { + classpathEntryObj = Paths.get(FastPathResolver.resolve(classpathEntryURL.toString())); + } catch (final IllegalArgumentException | SecurityException e3) { + throw new IOException( + "Cannot handle URL " + classpathEntryURL + " : " + e3.getMessage()); + } + } + } catch (final FileSystemNotFoundException e) { + // This is a custom URL scheme without a backing FileSystem + } + } // else this is a remote jar URL + + } else if (classpathEntryObj instanceof URI) { + final URI classpathEntryURI = (URI) classpathEntryObj; + final String scheme = classpathEntryURI.getScheme(); + if ("http".equals(scheme) || "https".equals(scheme)) { + // Jar URL or URI (remote URLs/URIs must be jars) + return classpathEntryURI; + } else { + try { + // See if the URI resolves to a file or directory via the Path API + classpathEntryObj = Paths.get(classpathEntryURI); + } catch (final IllegalArgumentException | SecurityException e) { + // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" + // for paths like "jar:file:myjar.jar!/" (#625) + try { + classpathEntryObj = new File(classpathEntryURI).toPath(); + } catch (final IllegalArgumentException e2) { + // Try normalizing path (which would reduce this to simply "myjar.jar") + // and then passing it again to Paths.get. + try { + classpathEntryObj = Paths.get(FastPathResolver.resolve(classpathEntryURI.toString())); + } catch (final IllegalArgumentException | SecurityException e3) { + throw new IOException( + "Cannot handle URI " + classpathEntryURI + " : " + e3.getMessage()); + } + } + } catch (final FileSystemNotFoundException e) { + // This is a custom URI scheme without a backing FileSystem + return classpathEntryURI; + } + } + } + + // Canonicalize Path objects so the same file is opened only once + if (classpathEntryObj instanceof Path) { + try { + // Canonicalize path, to avoid duplication + // Throws IOException if the file does not exist or an I/O error occurs + classpathEntryObj = ((Path) classpathEntryObj).toRealPath(); + } catch (final SecurityException e) { + // Ignore + } + } + + return classpathEntryObj; + } + + // ------------------------------------------------------------------------------------------------------------- + /** * A singleton map used to eliminate creation of duplicate {@link ClasspathElement} objects, to reduce the * chance that resources are scanned twice, by mapping canonicalized Path objects, URLs, etc. to @@ -375,357 +510,109 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader classpathEntryObjToClasspathEntrySingletonMap = // new SingletonMap() { @Override - public ClasspathElement newInstance(final Object key, final LogNode log) + public ClasspathElement newInstance(final Object classpathEntryObj, final LogNode log) throws IOException, InterruptedException { - // Each use of this map provides a NewInstanceFactory - throw new IOException("This method should not be called"); + // Overridden by a NewInstanceFactory + throw new IOException("Should not reach here"); } }; - /** - * The classpath element singleton map. For each classpath element path, canonicalize path, and create a - * ClasspathElement singleton. - */ - private final SingletonMap // - classpathEntryWorkUnitToClasspathElementSingletonMap = // - new SingletonMap() { - @Override - public ClasspathElement newInstance(final ClasspathEntryWorkUnit classpathEntryWorkUnit, - final LogNode log) throws IOException, InterruptedException { - Object classpathEntObj = classpathEntryWorkUnit.classpathEntryObj; - if (classpathEntObj == null) { - // Should not happen - throw new IOException("Got null classpath entry object"); - } - - // Convert URL/URI into String, to handle some normalization. - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end. - // Also strip any "jar:file:" or "file:" off the beginning. - // This normalizes "file:x.jar" and "x.jar" to the same string, for example. - if (classpathEntObj instanceof URL || classpathEntObj instanceof URI) { - classpathEntObj = FastPathResolver.resolve(classpathEntObj.toString()); - } - - // If classpath entry object is a URL-formatted string, convert to (or back to) a URL instance. - if (classpathEntObj instanceof String) { - String classpathEntStr = (String) classpathEntObj; - final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntStr).matches(); - final boolean isMultiSection = classpathEntStr.contains("!"); - if (isURL || isMultiSection) { - // Convert back to URL (or URI) if this has a URL scheme or if this is a multi-section - // path (which needs the "jar:file:" scheme) - if (!isURL) { - // Add "file:" scheme if there is no scheme - classpathEntStr = "file:" + classpathEntStr; - } - if (isMultiSection) { - // Multi-section URL strings that do not already have a URL scheme need to - // have the "jar:file:" scheme - classpathEntStr = "jar:" + classpathEntStr; - // Also "jar:" URLs need at least one instance of "!/" -- if only "!" is used - // without a subsequent "/", replace it - classpathEntStr = classpathEntStr.replaceAll("!([^/])", "!/$1"); - } - try { - // Convert classpath entry to (or back to) a URL. - classpathEntObj = new URL(classpathEntStr); - } catch (final MalformedURLException e) { - // Try creating URI if URL creation fails, in case there is a URI-only scheme - try { - classpathEntObj = new URI(classpathEntStr); - } catch (final URISyntaxException e1) { - throw new IOException("Malformed URI: " + classpathEntObj + " : " + e1); - } - } - } else { - // If this is not a URL with a non-"file:" scheme and is not a multi-section URL, - // try parsing the path string as a Path - try { - classpathEntObj = Paths.get(classpathEntStr); - } catch (final InvalidPathException e) { - throw new IOException("Malformed path: " + classpathEntObj + " : " + e); - } - } - } - // At this point, String is dealt with and String, URL, and URI classpath elements are all - // normalized together. classpathEntObj is either a URL, URI, or Path. - - // Check type of classpath entry object - Path classpathEntryPath = null; - if (classpathEntObj instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntObj; - // TODO scheme can be "jar" - final String scheme = classpathEntryURL.getProtocol(); - if ("http".equals(scheme) || "https".equals(scheme)) { - // Jar URL or URI (remote URLs/URIs must be jars) - try { - return classpathEntryObjToClasspathEntrySingletonMap.get( - // Use toString() for the key so that URLs and URIs that resolve to the - // same resource will point to the same entry in the singleton map - classpathEntryURL.toString(), log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - return new ClasspathElementZip(classpathEntryURL, - classpathEntryWorkUnit, nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e) { - throw new IOException("Could not open URL: " + classpathEntryURL + " : " + e); - } - } else { - try { - // See if the URL resolves to a file or directory via the Path API - classpathEntryPath = Paths.get(classpathEntryURL.toURI()); - } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) - try { - classpathEntryPath = new File(classpathEntryURL.toURI()).toPath(); - } catch (IllegalArgumentException | URISyntaxException e2) { - // Try normalizing path (which would reduce this to simply "myjar.jar") - // and then passing it again to Paths.get. - try { - classpathEntryPath = Paths - .get(FastPathResolver.resolve(classpathEntryURL.toString())); - } catch (final IllegalArgumentException | SecurityException e3) { - throw new IOException( - "Cannot handle URL " + classpathEntryURL + " : " + e3.getMessage()); - } - } - } catch (final FileSystemNotFoundException e) { - // This is a custom URL scheme without a backing FileSystem - try { - return classpathEntryObjToClasspathEntrySingletonMap.get( - // Use toString() for the key so that URLs and URIs that resolve to the - // same resource will point to the same entry in the singleton map - classpathEntryURL.toString(), log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - return new ClasspathElementZip(classpathEntryURL, - classpathEntryWorkUnit, nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open URL: " + classpathEntryURL + " : " + e2); - } - } - } - } else if (classpathEntObj instanceof URI) { - final URI classpathEntryURI = (URI) classpathEntObj; - final String scheme = classpathEntryURI.getScheme(); - if ("http".equals(scheme) || "https".equals(scheme)) { - // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryURI, classpathEntryWorkUnit, - nestedJarHandler, scanSpec); - } else { - try { - // See if the URI resolves to a file or directory via the Path API - classpathEntryPath = Paths.get(classpathEntryURI); - } catch (final IllegalArgumentException | SecurityException e) { - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) - try { - classpathEntryPath = new File(classpathEntryURI).toPath(); - } catch (final IllegalArgumentException e2) { - // Try normalizing path (which would reduce this to simply "myjar.jar") - // and then passing it again to Paths.get. - try { - classpathEntryPath = Paths - .get(FastPathResolver.resolve(classpathEntryURI.toString())); - } catch (final IllegalArgumentException | SecurityException e3) { - throw new IOException( - "Cannot handle URI " + classpathEntryURI + " : " + e3.getMessage()); - } - } - } catch (final FileSystemNotFoundException e) { - // This is a custom URI scheme without a backing FileSystem - try { - return classpathEntryObjToClasspathEntrySingletonMap.get( - // Use toString() for the key so that URLs and URIs that resolve to the - // same resource will point to the same entry in the singleton map - classpathEntryURI.toString(), log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - return new ClasspathElementZip(classpathEntryURI, - classpathEntryWorkUnit, nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open URI: " + classpathEntryURI + " : " + e2); - } - } - } - } else if (classpathEntObj instanceof Path) { - classpathEntryPath = (Path) classpathEntObj; - } else { - // Fall through for any other object type (toString will be used to get path) - } - - if (classpathEntryPath != null) { - try { - // Canonicalize path, to avoid duplication - // Throws IOException if the file does not exist or an I/O error occurs - classpathEntryPath = classpathEntryPath.toRealPath(); - } catch (final SecurityException e) { - // Ignore - } - if (FileUtils.canReadAndIsFile(classpathEntryPath)) { - // packageRootPath is a Path which points to a lib/ext jar inside a parent Path - try { - final Path path = classpathEntryPath; - return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - return new ClasspathElementZip(path, classpathEntryWorkUnit, - nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); - } - } else if (FileUtils.canReadAndIsDir(classpathEntryPath)) { - if ("JrtFileSystem" - .equals(classpathEntryPath.getFileSystem().getClass().getSimpleName())) { - // Ignore JrtFileSystem (#553) -- paths are of form: - // /modules/java.base/module-info.class - throw new IOException("Ignoring JrtFS filesystem path " + classpathEntryPath - + " (modules are scanned using the JPMS API)"); - } - // classpathEntryObj is a Path which points to a dir -- need to scan it recursively - try { - final Path path = classpathEntryPath; - return classpathEntryObjToClasspathEntrySingletonMap.get(classpathEntryPath, log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - return new ClasspathElementDir(path, classpathEntryWorkUnit, - nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + classpathEntryPath + " : " + e2); - } - } - } - - // Fall through for other object types (including String) - // Convert classpathEntryObj to a string - final String classpathEntryPathStr = classpathEntObj.toString(); - - // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators - final String pathNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), - classpathEntryPathStr); - - // Strip everything after first "!", to get path of base jarfile or dir - final int plingIdx = pathNormalized.indexOf('!'); - final String pathToCanonicalize = plingIdx < 0 ? pathNormalized - : pathNormalized.substring(0, plingIdx); - // Canonicalize base jarfile or dir (may throw IOException) - final File fileCanonicalized = new File(pathToCanonicalize).getCanonicalFile(); - // Test if base file or dir exists (and is a standard file or dir) - if (!fileCanonicalized.exists()) { - throw new FileNotFoundException(); - } - if (!FileUtils.canRead(fileCanonicalized)) { - throw new IOException("Cannot read file or directory"); - } - boolean isJar = classpathEntryPathStr.regionMatches(true, 0, "jar:", 0, 4) || plingIdx > 0; - if (fileCanonicalized.isFile()) { - // If a file, must be a jar - isJar = true; - } else if (fileCanonicalized.isDirectory()) { - if (isJar) { - throw new IOException("Expected jar, found directory"); - } - } else { - throw new IOException("Not a normal file or directory"); - } - // Convert File into Path to try to merge dups - final Path pathCanonicalized; - try { - pathCanonicalized = fileCanonicalized.toPath(); - } catch (final InvalidPathException e) { - throw new IOException("Could not convert File to Path: " + fileCanonicalized + " : " + e); - } - try { - final boolean jar = isJar; - return classpathEntryObjToClasspathEntrySingletonMap.get(pathCanonicalized, log, - new NewInstanceFactory() { - @Override - public ClasspathElement newInstance() { - // Instantiate a ClasspathElementZip or ClasspathElementDir singleton - // for the classpath element path - return jar - ? new ClasspathElementZip(pathCanonicalized, classpathEntryWorkUnit, - nestedJarHandler, scanSpec) - : new ClasspathElementDir(pathCanonicalized, classpathEntryWorkUnit, - nestedJarHandler, scanSpec); - } - }); - } catch (InterruptedException | NullSingletonException | NewInstanceException e2) { - throw new IOException("Could not open path: " + pathCanonicalized + " : " + e2); - } - } - }; + // ------------------------------------------------------------------------------------------------------------- /** * Create a WorkUnitProcessor for opening traditional classpath entries (which are mapped to * {@link ClasspathElementDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * - * @param allClasspathElts + * @param allClasspathEltsOut * on exit, the set of all classpath elements - * @param toplevelClasspathElts + * @param toplevelClasspathEltsOut * on exit, the toplevel classpath elements * @param ClasspathEltOrder * the toplevel classpath elt order * @return the work unit processor */ private WorkUnitProcessor newClasspathEntryWorkUnitProcessor( - final Set allClasspathElts, final Set toplevelClasspathElts) { + final Set allClasspathEltsOut, final Set toplevelClasspathEltsOut) { return new WorkUnitProcessor() { @Override public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final WorkQueue workQueue, final LogNode log) throws InterruptedException { try { - // Create a ClasspathElementZip or ClasspathElementDir for each entry in the classpath - ClasspathElement classpathElt; - try { - // Get or create ClasspathElement from raw classpath entry object - classpathElt = classpathEntryWorkUnitToClasspathElementSingletonMap.get(workUnit, log); - } catch (final NullSingletonException | NewInstanceException e) { - throw new IOException("Cannot get classpath element for classpath entry " - + workUnit.classpathEntryObj + " : " + (e.getCause() == null ? e : e.getCause())); - } + // Normalize the classpath entry object + workUnit.classpathEntryObj = normalizeClasspathEntry(workUnit.classpathEntryObj); - // Only run open() once per ClasspathElement - if (allClasspathElts.add(classpathElt)) { - final LogNode subLog = log == null ? null - : log.log("Opening classpath element " + classpathElt); - - // Check if the classpath element is valid (classpathElt.skipClasspathElement - // will be set if not). In case of ClasspathElementZip, open or extract nested - // jars as LogicalZipFile instances. Read manifest files for jarfiles to look - // for Class-Path manifest entries. Adds extra classpath elements to the work - // queue if they are found. - classpathElt.open(workQueue, subLog); - - if (workUnit.parentClasspathElement != null) { - // Link classpath element to its parent, if it is not a toplevel element - workUnit.parentClasspathElement.childClasspathElements.add(classpathElt); - } else { - toplevelClasspathElts.add(classpathElt); + // Determine if classpath entry is a jar or dir + boolean isJar = false; + if (workUnit.classpathEntryObj instanceof URL || workUnit.classpathEntryObj instanceof URI) { + // URLs and URIs always point to jars + isJar = true; + } else if (workUnit.classpathEntryObj instanceof Path) { + final Path path = (Path) workUnit.classpathEntryObj; + if (FileUtils.canReadAndIsFile(path)) { + // classpathEntObj is a Path which points to a file, so it must be a jar + isJar = true; + } else if (FileUtils.canReadAndIsDir(path)) { + if ("JrtFileSystem".equals(path.getFileSystem().getClass().getSimpleName())) { + // Ignore JrtFileSystem (#553) -- paths are of form: + // /modules/java.base/module-info.class + throw new IOException("Ignoring JrtFS filesystem path " + workUnit.classpathEntryObj + + " (modules are scanned using the JPMS API)"); + } + // classpathEntObj is a Path which points to a dir + } else if (!FileUtils.canRead(path)) { + throw new IOException("Cannot read path: " + path); } + } else { + // Should not happen + throw new IOException("Got unexpected classpath entry object type " + + workUnit.classpathEntryObj.getClass().getName() + " : " + + workUnit.classpathEntryObj); } - } catch (final IOException | SecurityException e) { + + // Create a ClasspathElementZip or ClasspathElementDir from the classpath entry + // Use a singleton map to ensure that classpath elements are only opened once + // per unique Path, URL, or URI + final boolean isJarFinal = isJar; + classpathEntryObjToClasspathEntrySingletonMap.get(workUnit.classpathEntryObj, log, + new NewInstanceFactory() { + @Override + public ClasspathElement newInstance() throws IOException, InterruptedException { + final ClasspathElement cpElt = isJarFinal + ? new ClasspathElementZip(workUnit.classpathEntryObj, workUnit, + nestedJarHandler, scanSpec) + : new ClasspathElementDir((Path) workUnit.classpathEntryObj, workUnit, + nestedJarHandler, scanSpec); + + allClasspathEltsOut.add(cpElt); + + // Run open() on the ClasspathElement + final LogNode subLog = log == null ? null + : log.log("Opening classpath element " + cpElt); + + // Check if the classpath element is valid (classpathElt.skipClasspathElement + // will be set if not). In case of ClasspathElementZip, open or extract nested + // jars as LogicalZipFile instances. Read manifest files for jarfiles to look + // for Class-Path manifest entries. Adds extra classpath elements to the work + // queue if they are found. + cpElt.open(workQueue, subLog); + + if (workUnit.parentClasspathElement != null) { + // Link classpath element to its parent, if it is not a toplevel element + workUnit.parentClasspathElement.childClasspathElements.add(cpElt); + } else { + toplevelClasspathEltsOut.add(cpElt); + } + + return cpElt; + } + }); + + } catch (final Exception e) { if (log != null) { - log.log("Skipping invalid classpath element " + workUnit.classpathEntryObj + " : " + e); + log.log("Skipping invalid classpath entry " + workUnit.classpathEntryObj + " : " + + (e.getCause() == null ? e : e.getCause())); } } } @@ -838,6 +725,7 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, final LogNode subLog = workUnit.classfileResource.scanLog == null ? null : workUnit.classfileResource.scanLog.log(workUnit.classfileResource.getPath(), "Parsing classfile"); + try { // Parse classfile binary format, creating a Classfile object final Classfile classfile = new Classfile(workUnit.classpathElement, classpathOrder, diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java index 1ab4b7844..61fdb4014 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -170,13 +170,13 @@ V get() throws InterruptedException { * The instance type. */ @FunctionalInterface - public interface NewInstanceFactory { + public interface NewInstanceFactory { /** * Create a new instance. * * @return The new instance. */ - public V newInstance(); + public V newInstance() throws E, InterruptedException; } /** @@ -209,7 +209,7 @@ public interface NewInstanceFactory { * @throws NewInstanceException * if {@link #newInstance(Object, LogNode)} threw an exception. */ - public V get(final K key, final LogNode log, final NewInstanceFactory newInstanceFactory) + public V get(final K key, final LogNode log, final NewInstanceFactory newInstanceFactory) throws E, InterruptedException, NullSingletonException, NewInstanceException { final SingletonHolder singletonHolder = map.get(key); @SuppressWarnings("null") diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index b248f2afc..9f4bfeca4 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -280,6 +280,24 @@ public static boolean canRead(final File file) { } } + /** + * Check if a {@link Path} exists and can be read. + * + * @param path + * A {@link Path}. + * @return true if the file exists and can be read. + */ + public static boolean canRead(final Path path) { + try { + if (!Files.isReadable(path)) { + return false; + } + } catch (final SecurityException e) { + return false; + } + return Files.isRegularFile(path); + } + /** * Check if a {@link File} exists, is a regular file, and can be read. * @@ -307,7 +325,7 @@ public static boolean canReadAndIsFile(final File file) { */ public static boolean canReadAndIsFile(final Path path) { try { - if (!Files.exists(path)) { + if (!Files.isReadable(path)) { return false; } } catch (final SecurityException e) { @@ -347,7 +365,7 @@ public static void checkCanReadAndIsFile(final File file) throws IOException { */ public static void checkCanReadAndIsFile(final Path path) throws IOException { try { - if (!Files.exists(path)) { + if (!Files.isReadable(path)) { throw new FileNotFoundException("Path does not exist or cannot be read: " + path); } } catch (final SecurityException e) { @@ -385,7 +403,7 @@ public static boolean canReadAndIsDir(final File file) { */ public static boolean canReadAndIsDir(final Path path) { try { - if (!Files.exists(path)) { + if (!Files.isReadable(path)) { return false; } } catch (final SecurityException e) { diff --git a/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java index d2733cc80..cd8ecfb91 100644 --- a/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java +++ b/src/test/java/io/github/classgraph/issues/issue673/Issue673Test.java @@ -3,6 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import java.net.URL; +import java.util.Arrays; +import java.util.stream.Collectors; import org.junit.jupiter.api.Test; @@ -20,11 +22,15 @@ void testResourcesCanBeRead() { // This succeeded before issue 673 was fixed try (ScanResult scanResult = new ClassGraph().overrideClasspath(bURL, aURL).scan()) { + assertThat(scanResult.getClasspathFiles().stream().map(f -> f.getName()).collect(Collectors.toList())) + .isEqualTo(Arrays.asList("b.zip", "c.zip", "a.zip")); assertThat(scanResult.getAllResources().getPaths()).contains("C"); } // This failed before issue 673 was fixed try (ScanResult scanResult = new ClassGraph().overrideClasspath(aURL, bURL).scan()) { + assertThat(scanResult.getClasspathFiles().stream().map(f -> f.getName()).collect(Collectors.toList())) + .isEqualTo(Arrays.asList("a.zip", "b.zip", "c.zip")); assertThat(scanResult.getAllResources().getPaths()).contains("C"); } } From 4a93ef6da247b41f88a48792a6e161613c206acd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 00:53:46 -0600 Subject: [PATCH 394/713] [maven-release-plugin] prepare release classgraph-4.8.144 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3c21bd897..4ff62d97d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.144-SNAPSHOT + 4.8.144 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.142 + classgraph-4.8.144 From 5c2f20014be5745129fc70689522b2bdb1396fa2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 00:54:29 -0600 Subject: [PATCH 395/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ff62d97d..a2136dd50 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.144 + 4.8.145-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 90310ad182f86a2a76d87c7491ce8c6fc8222f45 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 01:07:20 -0600 Subject: [PATCH 396/713] Update comments --- .../java/io/github/classgraph/Scanner.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 2af3f1215..b8622fc8f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -366,11 +366,23 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader // ------------------------------------------------------------------------------------------------------------- - private static Object normalizeClasspathEntry(Object classpathEntryObj) throws IOException { - if (classpathEntryObj == null) { + /** + * Normalize a classpath entry object so that it is mapped to a canonical {@link Path} object if possible, + * falling back to a {@link URL} or {@link URI} if not possible. This is needed to avoid treating + * "file:///path/to/x.jar" and "/path/to/x.jar" as different classpath elements. Maps URL("jar:file:x.jar!/") to + * Path("x.jar"), etc. + * + * @param classpathEntObj + * The classpath entry object. + * @return The normalized classpath entry object. + * @throws IOException + */ + private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOException { + if (classpathEntObj == null) { // Should not happen throw new IOException("Got null classpath entry object"); } + Object classpathEntryObj = classpathEntObj; // Convert URL/URI (or anything other than URL/URI, or Path) into a String. // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" @@ -576,6 +588,8 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // per unique Path, URL, or URI final boolean isJarFinal = isJar; classpathEntryObjToClasspathEntrySingletonMap.get(workUnit.classpathEntryObj, log, + // A NewInstanceFactory is used here because workUnit has to be passed in, + // and the standard newInstance API doesn't support an extra parameter like this new NewInstanceFactory() { @Override public ClasspathElement newInstance() throws IOException, InterruptedException { From 23f4ef2ba7916181646e325bf932750529906cc5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 02:16:29 -0600 Subject: [PATCH 397/713] Windows compat fixes --- .../java/io/github/classgraph/Scanner.java | 77 +++++++------------ 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index b8622fc8f..87f3fc522 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -382,20 +382,21 @@ private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOE // Should not happen throw new IOException("Got null classpath entry object"); } - Object classpathEntryObj = classpathEntObj; + Object classpathEntryObjNormalized = classpathEntObj; // Convert URL/URI (or anything other than URL/URI, or Path) into a String. // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" // for paths like "jar:file:myjar.jar!/" (#625) -- need to strip the "!/" off the end. // Also strip any "jar:file:" or "file:" off the beginning. // This normalizes "file:x.jar" and "x.jar" to the same string, for example. - if (!(classpathEntryObj instanceof Path)) { - classpathEntryObj = FastPathResolver.resolve(FileUtils.currDirPath(), classpathEntryObj.toString()); + if (!(classpathEntryObjNormalized instanceof Path)) { + classpathEntryObjNormalized = FastPathResolver.resolve(FileUtils.currDirPath(), + classpathEntryObjNormalized.toString()); } // If classpath entry object is a URL-formatted string, convert to (or back to) a URL instance. - if (classpathEntryObj instanceof String) { - String classpathEntStr = (String) classpathEntryObj; + if (classpathEntryObjNormalized instanceof String) { + String classpathEntStr = (String) classpathEntryObjNormalized; final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntStr).matches(); final boolean isMultiSection = classpathEntStr.contains("!"); if (isURL || isMultiSection) { @@ -415,58 +416,46 @@ private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOE } try { // Convert classpath entry to (or back to) a URL. - classpathEntryObj = new URL(classpathEntStr); + classpathEntryObjNormalized = new URL(classpathEntStr); } catch (final MalformedURLException e) { // Try creating URI if URL creation fails, in case there is a URI-only scheme try { - classpathEntryObj = new URI(classpathEntStr); + classpathEntryObjNormalized = new URI(classpathEntStr); } catch (final URISyntaxException e1) { - throw new IOException("Malformed URI: " + classpathEntryObj + " : " + e1); + throw new IOException("Malformed URI: " + classpathEntryObjNormalized + " : " + e1); } } } // Last-ditch effort -- try to convert String to Path - if (classpathEntryObj instanceof String) { + if (classpathEntryObjNormalized instanceof String) { try { - classpathEntryObj = Paths.get((String) classpathEntryObj); + classpathEntryObjNormalized = Paths.get((String) classpathEntryObjNormalized); } catch (final InvalidPathException e) { - throw new IOException("Malformed path: " + classpathEntryObj + " : " + e); + throw new IOException("Malformed path: " + classpathEntryObjNormalized + " : " + e); } } } // At this point, String is dealt with and String, URL, and URI classpath elements are all // normalized together. classpathEntObj is either a URL, URI, or Path. - // Check type of classpath entry object - if (classpathEntryObj instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntryObj; + // If classpath entry object is a URL or a URI, try seeing if it can be mapped to a Path + if (classpathEntryObjNormalized instanceof URL) { + final URL classpathEntryURL = (URL) classpathEntryObjNormalized; final String scheme = classpathEntryURL.getProtocol(); if (!"http".equals(scheme) && !"https".equals(scheme)) { try { // See if the URL resolves to a file or directory via the Path API - classpathEntryObj = Paths.get(classpathEntryURL.toURI()); + classpathEntryObjNormalized = Paths.get(classpathEntryURL.toURI()); } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) - try { - classpathEntryObj = new File(classpathEntryURL.toURI()).toPath(); - } catch (IllegalArgumentException | URISyntaxException e2) { - // Try normalizing path (which would reduce this to simply "myjar.jar") - // and then passing it again to Paths.get. - try { - classpathEntryObj = Paths.get(FastPathResolver.resolve(classpathEntryURL.toString())); - } catch (final IllegalArgumentException | SecurityException e3) { - throw new IOException( - "Cannot handle URL " + classpathEntryURL + " : " + e3.getMessage()); - } - } + // URI cannot be represented as a Path, so it probably is a multi-section URI + // (representing a nested jar, or a jar URI with a non-empty package root). } catch (final FileSystemNotFoundException e) { // This is a custom URL scheme without a backing FileSystem } } // else this is a remote jar URL - } else if (classpathEntryObj instanceof URI) { - final URI classpathEntryURI = (URI) classpathEntryObj; + } else if (classpathEntryObjNormalized instanceof URI) { + final URI classpathEntryURI = (URI) classpathEntryObjNormalized; final String scheme = classpathEntryURI.getScheme(); if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) @@ -474,41 +463,29 @@ private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOE } else { try { // See if the URI resolves to a file or directory via the Path API - classpathEntryObj = Paths.get(classpathEntryURI); + classpathEntryObjNormalized = Paths.get(classpathEntryURI); } catch (final IllegalArgumentException | SecurityException e) { - // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" - // for paths like "jar:file:myjar.jar!/" (#625) - try { - classpathEntryObj = new File(classpathEntryURI).toPath(); - } catch (final IllegalArgumentException e2) { - // Try normalizing path (which would reduce this to simply "myjar.jar") - // and then passing it again to Paths.get. - try { - classpathEntryObj = Paths.get(FastPathResolver.resolve(classpathEntryURI.toString())); - } catch (final IllegalArgumentException | SecurityException e3) { - throw new IOException( - "Cannot handle URI " + classpathEntryURI + " : " + e3.getMessage()); - } - } + // URL cannot be represented as a Path, so it probably is a multi-section URL + // (representing a nested jar, or a jar URL with a non-empty package root). } catch (final FileSystemNotFoundException e) { // This is a custom URI scheme without a backing FileSystem return classpathEntryURI; } - } + } // else this is a remote jar URL } // Canonicalize Path objects so the same file is opened only once - if (classpathEntryObj instanceof Path) { + if (classpathEntryObjNormalized instanceof Path) { try { // Canonicalize path, to avoid duplication // Throws IOException if the file does not exist or an I/O error occurs - classpathEntryObj = ((Path) classpathEntryObj).toRealPath(); + classpathEntryObjNormalized = ((Path) classpathEntryObjNormalized).toRealPath(); } catch (final SecurityException e) { // Ignore } } - return classpathEntryObj; + return classpathEntryObjNormalized; } // ------------------------------------------------------------------------------------------------------------- From aeb240cd5d095b6532b574050ed3d115059ac4a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 02:25:52 -0600 Subject: [PATCH 398/713] Windows compat fixes --- .../java/io/github/classgraph/Scanner.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 87f3fc522..6b26d764f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -377,7 +377,7 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader * @return The normalized classpath entry object. * @throws IOException */ - private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOException { + private static Object normalizeClasspathEntry(final Object classpathEntObj) throws IOException { if (classpathEntObj == null) { // Should not happen throw new IOException("Got null classpath entry object"); @@ -444,23 +444,25 @@ private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOE final String scheme = classpathEntryURL.getProtocol(); if (!"http".equals(scheme) && !"https".equals(scheme)) { try { - // See if the URL resolves to a file or directory via the Path API - classpathEntryObjNormalized = Paths.get(classpathEntryURL.toURI()); - } catch (final IllegalArgumentException | SecurityException | URISyntaxException e) { - // URI cannot be represented as a Path, so it probably is a multi-section URI - // (representing a nested jar, or a jar URI with a non-empty package root). - } catch (final FileSystemNotFoundException e) { - // This is a custom URL scheme without a backing FileSystem + final URI classpathEntryURI = classpathEntryURL.toURI(); + try { + // See if the URL resolves to a file or directory via the Path API + classpathEntryObjNormalized = Paths.get(classpathEntryURI); + } catch (final IllegalArgumentException | SecurityException e) { + // URI cannot be represented as a Path, so it probably is a multi-section URI + // (representing a nested jar, or a jar URI with a non-empty package root). + } catch (final FileSystemNotFoundException e) { + // This is a custom URL scheme without a backing FileSystem + } + } catch (final URISyntaxException e1) { + // URL doesn't work as a URI for some reason } } // else this is a remote jar URL } else if (classpathEntryObjNormalized instanceof URI) { final URI classpathEntryURI = (URI) classpathEntryObjNormalized; final String scheme = classpathEntryURI.getScheme(); - if ("http".equals(scheme) || "https".equals(scheme)) { - // Jar URL or URI (remote URLs/URIs must be jars) - return classpathEntryURI; - } else { + if (!"http".equals(scheme) && !"https".equals(scheme)) { try { // See if the URI resolves to a file or directory via the Path API classpathEntryObjNormalized = Paths.get(classpathEntryURI); @@ -469,7 +471,6 @@ private static Object normalizeClasspathEntry(Object classpathEntObj) throws IOE // (representing a nested jar, or a jar URL with a non-empty package root). } catch (final FileSystemNotFoundException e) { // This is a custom URI scheme without a backing FileSystem - return classpathEntryURI; } } // else this is a remote jar URL } From 5edb20f3579fa81e44e5e1883300d0b059757251 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 02:28:00 -0600 Subject: [PATCH 399/713] [maven-release-plugin] prepare release classgraph-4.8.145 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a2136dd50..7265dc406 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.145-SNAPSHOT + 4.8.145 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.145 From 851d932f93836a584ff8d77402a99b1caebfd0a3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 02:28:03 -0600 Subject: [PATCH 400/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7265dc406..aa08efd27 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.145 + 4.8.146-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.145 + classgraph-4.8.144 From 1dd28900801dc9b6bfe6d9f63c8bfab608beca1f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 02:32:51 -0600 Subject: [PATCH 401/713] Code simplification --- .../java/io/github/classgraph/Scanner.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 6b26d764f..0754e273a 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -445,17 +445,13 @@ private static Object normalizeClasspathEntry(final Object classpathEntObj) thro if (!"http".equals(scheme) && !"https".equals(scheme)) { try { final URI classpathEntryURI = classpathEntryURL.toURI(); - try { - // See if the URL resolves to a file or directory via the Path API - classpathEntryObjNormalized = Paths.get(classpathEntryURI); - } catch (final IllegalArgumentException | SecurityException e) { - // URI cannot be represented as a Path, so it probably is a multi-section URI - // (representing a nested jar, or a jar URI with a non-empty package root). - } catch (final FileSystemNotFoundException e) { - // This is a custom URL scheme without a backing FileSystem - } - } catch (final URISyntaxException e1) { - // URL doesn't work as a URI for some reason + // See if the URL resolves to a file or directory via the Path API + classpathEntryObjNormalized = Paths.get(classpathEntryURI); + } catch (final URISyntaxException | IllegalArgumentException | SecurityException e1) { + // URI cannot be represented as a URI or as a Path, so it probably is a multi-section URI + // (representing a nested jar, or a jar URI with a non-empty package root). + } catch (final FileSystemNotFoundException e) { + // This is a custom URL scheme without a backing FileSystem } } // else this is a remote jar URL From beed50d548fa3f3a8879ffaa7173630b548a9f6e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:01:17 -0600 Subject: [PATCH 402/713] Code cleanup --- .../java/io/github/classgraph/Scanner.java | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 0754e273a..fcbe2b2a2 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -372,17 +372,17 @@ public ClasspathEntryWorkUnit(final Object classpathEntryObj, final ClassLoader * "file:///path/to/x.jar" and "/path/to/x.jar" as different classpath elements. Maps URL("jar:file:x.jar!/") to * Path("x.jar"), etc. * - * @param classpathEntObj + * @param classpathEntryObj * The classpath entry object. * @return The normalized classpath entry object. * @throws IOException */ - private static Object normalizeClasspathEntry(final Object classpathEntObj) throws IOException { - if (classpathEntObj == null) { + private static Object normalizeClasspathEntry(final Object classpathEntryObj) throws IOException { + if (classpathEntryObj == null) { // Should not happen throw new IOException("Got null classpath entry object"); } - Object classpathEntryObjNormalized = classpathEntObj; + Object classpathEntryObjNormalized = classpathEntryObj; // Convert URL/URI (or anything other than URL/URI, or Path) into a String. // Paths.get fails with "IllegalArgumentException: URI is not hierarchical" @@ -416,13 +416,43 @@ private static Object normalizeClasspathEntry(final Object classpathEntObj) thro } try { // Convert classpath entry to (or back to) a URL. - classpathEntryObjNormalized = new URL(classpathEntStr); + final URL classpathEntryURL = new URL(classpathEntStr); + classpathEntryObjNormalized = classpathEntryURL; + + // If this is not a multi-section URL, try converting URL to a Path + if (!isMultiSection) { + try { + final String scheme = classpathEntryURL.getProtocol(); + if (!"http".equals(scheme) && !"https".equals(scheme)) { + final URI classpathEntryURI = classpathEntryURL.toURI(); + // See if the URL resolves to a file or directory via the Path API + classpathEntryObjNormalized = Paths.get(classpathEntryURI); + } + } catch (final URISyntaxException | IllegalArgumentException | SecurityException e1) { + // URL cannot be represented as a URI or as a Path + } catch (final FileSystemNotFoundException e) { + // This is a custom URL scheme without a backing FileSystem + } + } // else this is a remote jar URL + } catch (final MalformedURLException e) { // Try creating URI if URL creation fails, in case there is a URI-only scheme try { - classpathEntryObjNormalized = new URI(classpathEntStr); + final URI classpathEntryURI = new URI(classpathEntStr); + classpathEntryObjNormalized = classpathEntryURI; + + final String scheme = classpathEntryURI.getScheme(); + if (!"http".equals(scheme) && !"https".equals(scheme)) { + // See if the URI resolves to a file or directory via the Path API + classpathEntryObjNormalized = Paths.get(classpathEntryURI); + } // else this is a remote jar URI + } catch (final URISyntaxException e1) { throw new IOException("Malformed URI: " + classpathEntryObjNormalized + " : " + e1); + } catch (final IllegalArgumentException | SecurityException e1) { + // URI cannot be represented as a Path + } catch (final FileSystemNotFoundException e1) { + // This is a custom URI scheme without a backing FileSystem } } } @@ -431,45 +461,14 @@ private static Object normalizeClasspathEntry(final Object classpathEntObj) thro try { classpathEntryObjNormalized = Paths.get((String) classpathEntryObjNormalized); } catch (final InvalidPathException e) { - throw new IOException("Malformed path: " + classpathEntryObjNormalized + " : " + e); + throw new IOException("Malformed path: " + classpathEntryObj + " : " + e); } } } - // At this point, String is dealt with and String, URL, and URI classpath elements are all - // normalized together. classpathEntObj is either a URL, URI, or Path. - - // If classpath entry object is a URL or a URI, try seeing if it can be mapped to a Path - if (classpathEntryObjNormalized instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntryObjNormalized; - final String scheme = classpathEntryURL.getProtocol(); - if (!"http".equals(scheme) && !"https".equals(scheme)) { - try { - final URI classpathEntryURI = classpathEntryURL.toURI(); - // See if the URL resolves to a file or directory via the Path API - classpathEntryObjNormalized = Paths.get(classpathEntryURI); - } catch (final URISyntaxException | IllegalArgumentException | SecurityException e1) { - // URI cannot be represented as a URI or as a Path, so it probably is a multi-section URI - // (representing a nested jar, or a jar URI with a non-empty package root). - } catch (final FileSystemNotFoundException e) { - // This is a custom URL scheme without a backing FileSystem - } - } // else this is a remote jar URL - - } else if (classpathEntryObjNormalized instanceof URI) { - final URI classpathEntryURI = (URI) classpathEntryObjNormalized; - final String scheme = classpathEntryURI.getScheme(); - if (!"http".equals(scheme) && !"https".equals(scheme)) { - try { - // See if the URI resolves to a file or directory via the Path API - classpathEntryObjNormalized = Paths.get(classpathEntryURI); - } catch (final IllegalArgumentException | SecurityException e) { - // URL cannot be represented as a Path, so it probably is a multi-section URL - // (representing a nested jar, or a jar URL with a non-empty package root). - } catch (final FileSystemNotFoundException e) { - // This is a custom URI scheme without a backing FileSystem - } - } // else this is a remote jar URL - } + // At this point, classpathEntryObjNormalized is either a Path wherever possible (where the + // classpath entry pointed to a jarfile or directory) or a URL/URI (for multi-section "jar:" + // URLs with "!" separators, custom URL schemes without backing filesystems, or URLs that + // can't be turned into a Path for any other reason). // Canonicalize Path objects so the same file is opened only once if (classpathEntryObjNormalized instanceof Path) { From e556adeff90244238a8ba93ef45fec3b58c2af93 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:06:23 -0600 Subject: [PATCH 403/713] Code cleanup; JrtFileSystem fix --- src/main/java/io/github/classgraph/Scanner.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index fcbe2b2a2..c3d69dbdd 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -529,25 +529,28 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, workUnit.classpathEntryObj = normalizeClasspathEntry(workUnit.classpathEntryObj); // Determine if classpath entry is a jar or dir - boolean isJar = false; + boolean isJar; if (workUnit.classpathEntryObj instanceof URL || workUnit.classpathEntryObj instanceof URI) { // URLs and URIs always point to jars isJar = true; } else if (workUnit.classpathEntryObj instanceof Path) { final Path path = (Path) workUnit.classpathEntryObj; + if ("JrtFileSystem".equals(path.getFileSystem().getClass().getSimpleName())) { + // Ignore JrtFileSystem (#553) -- paths are of form: + // /modules/java.base/module-info.class + throw new IOException("Ignoring JrtFS filesystem path " + + "(modules are scanned using the JPMS API): " + path); + } if (FileUtils.canReadAndIsFile(path)) { // classpathEntObj is a Path which points to a file, so it must be a jar isJar = true; } else if (FileUtils.canReadAndIsDir(path)) { - if ("JrtFileSystem".equals(path.getFileSystem().getClass().getSimpleName())) { - // Ignore JrtFileSystem (#553) -- paths are of form: - // /modules/java.base/module-info.class - throw new IOException("Ignoring JrtFS filesystem path " + workUnit.classpathEntryObj - + " (modules are scanned using the JPMS API)"); - } // classpathEntObj is a Path which points to a dir + isJar = false; } else if (!FileUtils.canRead(path)) { throw new IOException("Cannot read path: " + path); + } else { + throw new IOException("Not a file or directory: " + path); } } else { // Should not happen From c94322d0f72b2587a887e75d549310ef60efb96d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:06:48 -0600 Subject: [PATCH 404/713] [maven-release-plugin] prepare release classgraph-4.8.146 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aa08efd27..0b1e71547 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.146-SNAPSHOT + 4.8.146 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.146 From 46910c7c16be6c1ac8aa320bc00c0f37b5b66907 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:06:51 -0600 Subject: [PATCH 405/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0b1e71547..6a8fbb5a2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.146 + 4.8.147-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.146 + classgraph-4.8.144 From c63a4bdc5fd3b42c9e7253613e5a27870b4e7227 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:12:55 -0600 Subject: [PATCH 406/713] Code cleanup; JrtFileSystem fix --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 6 +++--- src/main/java/io/github/classgraph/ClasspathElementZip.java | 6 ++++-- src/main/java/io/github/classgraph/Scanner.java | 6 ++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index d2f421220..e49716901 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -84,10 +84,10 @@ class ClasspathElementDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + ClasspathElementDir(final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, + final ScanSpec scanSpec) { super(workUnit, scanSpec); - this.classpathEltPath = classpathEltPath; + this.classpathEltPath = (Path) workUnit.classpathEntryObj; this.nestedJarHandler = nestedJarHandler; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7c0c3541f..515c18866 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -104,9 +104,11 @@ class ClasspathElementZip extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementZip(final Object rawPathObj, final ClasspathEntryWorkUnit workUnit, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + ClasspathElementZip(final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, + final ScanSpec scanSpec) { super(workUnit, scanSpec); + final Object rawPathObj = workUnit.classpathEntryObj; + // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index c3d69dbdd..b642e8ca6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -570,10 +570,8 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, @Override public ClasspathElement newInstance() throws IOException, InterruptedException { final ClasspathElement cpElt = isJarFinal - ? new ClasspathElementZip(workUnit.classpathEntryObj, workUnit, - nestedJarHandler, scanSpec) - : new ClasspathElementDir((Path) workUnit.classpathEntryObj, workUnit, - nestedJarHandler, scanSpec); + ? new ClasspathElementZip(workUnit, nestedJarHandler, scanSpec) + : new ClasspathElementDir(workUnit, nestedJarHandler, scanSpec); allClasspathEltsOut.add(cpElt); From 5a82673a19b560c834706741a81e42f182033281 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:12:55 -0600 Subject: [PATCH 407/713] Code cleanup --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 6 +++--- src/main/java/io/github/classgraph/ClasspathElementZip.java | 6 ++++-- src/main/java/io/github/classgraph/Scanner.java | 6 ++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index d2f421220..e49716901 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -84,10 +84,10 @@ class ClasspathElementDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementDir(final Path classpathEltPath, final ClasspathEntryWorkUnit workUnit, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + ClasspathElementDir(final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, + final ScanSpec scanSpec) { super(workUnit, scanSpec); - this.classpathEltPath = classpathEltPath; + this.classpathEltPath = (Path) workUnit.classpathEntryObj; this.nestedJarHandler = nestedJarHandler; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7c0c3541f..515c18866 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -104,9 +104,11 @@ class ClasspathElementZip extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementZip(final Object rawPathObj, final ClasspathEntryWorkUnit workUnit, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + ClasspathElementZip(final ClasspathEntryWorkUnit workUnit, final NestedJarHandler nestedJarHandler, + final ScanSpec scanSpec) { super(workUnit, scanSpec); + final Object rawPathObj = workUnit.classpathEntryObj; + // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index c3d69dbdd..b642e8ca6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -570,10 +570,8 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, @Override public ClasspathElement newInstance() throws IOException, InterruptedException { final ClasspathElement cpElt = isJarFinal - ? new ClasspathElementZip(workUnit.classpathEntryObj, workUnit, - nestedJarHandler, scanSpec) - : new ClasspathElementDir((Path) workUnit.classpathEntryObj, workUnit, - nestedJarHandler, scanSpec); + ? new ClasspathElementZip(workUnit, nestedJarHandler, scanSpec) + : new ClasspathElementDir(workUnit, nestedJarHandler, scanSpec); allClasspathEltsOut.add(cpElt); From 5e96f38148a28ab191828be63f486f23aeee54c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 15:19:30 -0600 Subject: [PATCH 408/713] Code cleanup --- .../github/classgraph/ClasspathElementDir.java | 4 +--- .../github/classgraph/ClasspathElementZip.java | 2 +- .../java/io/github/classgraph/Scanner.java | 18 +++++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index e49716901..ec9b9fb0f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -75,10 +75,8 @@ class ClasspathElementDir extends ClasspathElement { /** * A directory classpath element. * - * @param classpathEltPath - * the classpath element {@link Path} * @param workUnit - * the work unit + * the work unit -- workUnit.classpathEntryObj must be a {@link Path} object * @param nestedJarHandler * the nested jar handler * @param scanSpec diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 515c18866..7409d2c79 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -109,7 +109,7 @@ class ClasspathElementZip extends ClasspathElement { super(workUnit, scanSpec); final Object rawPathObj = workUnit.classpathEntryObj; - // Convert the raw path object (String, URL, URI, or Path) to a string. + // Convert the raw path object (Path, URL, or URI) to a string. // Any required URL/URI parsing are done in NestedJarHandler. String rawPath = null; if (rawPathObj instanceof Path) { diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index b642e8ca6..dec56b808 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -529,7 +529,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, workUnit.classpathEntryObj = normalizeClasspathEntry(workUnit.classpathEntryObj); // Determine if classpath entry is a jar or dir - boolean isJar; + final boolean isJar; if (workUnit.classpathEntryObj instanceof URL || workUnit.classpathEntryObj instanceof URI) { // URLs and URIs always point to jars isJar = true; @@ -562,38 +562,38 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // Create a ClasspathElementZip or ClasspathElementDir from the classpath entry // Use a singleton map to ensure that classpath elements are only opened once // per unique Path, URL, or URI - final boolean isJarFinal = isJar; classpathEntryObjToClasspathEntrySingletonMap.get(workUnit.classpathEntryObj, log, // A NewInstanceFactory is used here because workUnit has to be passed in, // and the standard newInstance API doesn't support an extra parameter like this new NewInstanceFactory() { @Override public ClasspathElement newInstance() throws IOException, InterruptedException { - final ClasspathElement cpElt = isJarFinal + final ClasspathElement classpathElement = isJar ? new ClasspathElementZip(workUnit, nestedJarHandler, scanSpec) : new ClasspathElementDir(workUnit, nestedJarHandler, scanSpec); - allClasspathEltsOut.add(cpElt); + allClasspathEltsOut.add(classpathElement); // Run open() on the ClasspathElement final LogNode subLog = log == null ? null - : log.log("Opening classpath element " + cpElt); + : log.log("Opening classpath element " + classpathElement); // Check if the classpath element is valid (classpathElt.skipClasspathElement // will be set if not). In case of ClasspathElementZip, open or extract nested // jars as LogicalZipFile instances. Read manifest files for jarfiles to look // for Class-Path manifest entries. Adds extra classpath elements to the work // queue if they are found. - cpElt.open(workQueue, subLog); + classpathElement.open(workQueue, subLog); if (workUnit.parentClasspathElement != null) { // Link classpath element to its parent, if it is not a toplevel element - workUnit.parentClasspathElement.childClasspathElements.add(cpElt); + workUnit.parentClasspathElement.childClasspathElements + .add(classpathElement); } else { - toplevelClasspathEltsOut.add(cpElt); + toplevelClasspathEltsOut.add(classpathElement); } - return cpElt; + return classpathElement; } }); From 7fb2bf354f535320be145e1349d6bd1e240c5131 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 16:34:46 -0600 Subject: [PATCH 409/713] Increase efficiency --- src/main/java/io/github/classgraph/Scanner.java | 2 +- .../nonapi/io/github/classgraph/utils/CollectionUtils.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index dec56b808..f549343d5 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -525,7 +525,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final WorkQueue workQueue, final LogNode log) throws InterruptedException { try { - // Normalize the classpath entry object + // Normalize the classpath entry object, and update it in the work unit workUnit.classpathEntryObj = normalizeClasspathEntry(workUnit.classpathEntryObj); // Determine if classpath entry is a jar or dir diff --git a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java index 9fc1ce89f..c3767c0f5 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java @@ -55,7 +55,7 @@ private CollectionUtils() { * the list */ public static > void sortIfNotEmpty(final List list) { - if (!list.isEmpty()) { + if (list.size() > 1) { Collections.sort(list); } } @@ -73,7 +73,7 @@ public static > void sortIfNotEmpty(final List void sortIfNotEmpty(final List list, final Comparator comparator) { - if (!list.isEmpty()) { + if (list.size() > 1) { Collections.sort(list, comparator); } } From d3dabdfb3a047bea7af27c188d68ff904ef02434 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 16:37:46 -0600 Subject: [PATCH 410/713] Check in debug code --- src/main/java/io/github/classgraph/Scanner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index f549343d5..96f418a16 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -1050,6 +1050,8 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, // Determine total ordering of classpath elements, inserting jars referenced in manifest Class-Path // entries in-place into the ordering, if they haven't been listed earlier in the classpath already. final List classpathEltOrder = findClasspathOrder(toplevelClasspathElts); + System.out.println("before: " + toplevelClasspathElts + "\n sorted: " + + CollectionUtils.sortCopy(toplevelClasspathElts) + "\n after: " + classpathEltOrder); // Find classpath elements that are path prefixes of other classpath elements, and for // ClasspathElementZip, get module-related manifest entry values From d38b27605d2abbe399763132d83acbaf93f87fdc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Apr 2022 16:43:26 -0600 Subject: [PATCH 411/713] Remove debug code --- src/main/java/io/github/classgraph/Scanner.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 96f418a16..f549343d5 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -1050,8 +1050,6 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, // Determine total ordering of classpath elements, inserting jars referenced in manifest Class-Path // entries in-place into the ordering, if they haven't been listed earlier in the classpath already. final List classpathEltOrder = findClasspathOrder(toplevelClasspathElts); - System.out.println("before: " + toplevelClasspathElts + "\n sorted: " - + CollectionUtils.sortCopy(toplevelClasspathElts) + "\n after: " + classpathEltOrder); // Find classpath elements that are path prefixes of other classpath elements, and for // ClasspathElementZip, get module-related manifest entry values From 9de0ab8048d0a0eb1d7b011c194d4b911c45de3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 02:00:52 +0000 Subject: [PATCH 412/713] Bump maven-javadoc-plugin from 3.3.2 to 3.4.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.3.2 to 3.4.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.3.2...maven-javadoc-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a8fbb5a2..0a892d7ad 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.2 + 3.4.0 org.apache.maven.plugins From e3de8ba61255693ec13d6a13fef0e279a5cfe26a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 02:00:57 +0000 Subject: [PATCH 413/713] Bump maven-site-plugin from 3.11.0 to 3.12.0 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.11.0 to 3.12.0. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.11.0...maven-site-plugin-3.12.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-site-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a8fbb5a2..d2bd18d29 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ org.apache.maven.plugins maven-site-plugin - 3.11.0 + 3.12.0 From 0466e8302719cc9a6fc0bd238a8f4a25d7a5a26b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Apr 2022 02:01:38 +0000 Subject: [PATCH 414/713] Bump maven-antrun-plugin from 3.0.0 to 3.1.0 Bumps [maven-antrun-plugin](https://github.com/apache/maven-antrun-plugin) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/apache/maven-antrun-plugin/releases) - [Commits](https://github.com/apache/maven-antrun-plugin/compare/maven-antrun-plugin-3.0.0...maven-antrun-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-antrun-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a8fbb5a2..aeec42a5c 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.apache.maven.plugins maven-antrun-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins From 8bd062afaf5fafa8028de407fb60605b99432993 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Apr 2022 02:01:40 +0000 Subject: [PATCH 415/713] Bump nexus-staging-maven-plugin from 1.6.12 to 1.6.13 Bumps nexus-staging-maven-plugin from 1.6.12 to 1.6.13. --- updated-dependencies: - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a8fbb5a2..47623900a 100644 --- a/pom.xml +++ b/pom.xml @@ -214,7 +214,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.12 + 1.6.13 org.apache.maven.plugins From 2e6e5a217dd39325d385e75d2a18d225835b6d73 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 May 2022 00:50:31 -0600 Subject: [PATCH 416/713] Fix lgtm Javadoc warnings --- src/main/java/io/github/classgraph/ClasspathElement.java | 4 ++-- src/main/java/io/github/classgraph/ClasspathElementZip.java | 3 --- src/main/java/io/github/classgraph/Scanner.java | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 683d07d36..55c5feef2 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -122,8 +122,8 @@ abstract class ClasspathElement implements Comparable { /** * A classpath element. * - * @param classLoader - * the classloader + * @param workUnit + * the work unit * @param scanSpec * the scan spec */ diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7409d2c79..d31621512 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -94,9 +94,6 @@ class ClasspathElementZip extends ClasspathElement { /** * A jarfile classpath element. * - * @param rawPathObj - * the raw path to the jarfile as a {@link String}, possibly including "!"-delimited nested paths, or - * a {@link URL}, {@link URI} ol {@link Path} for the jarfile. * @param workUnit * the work unit * @param nestedJarHandler diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index f549343d5..142e5aebd 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -513,8 +513,6 @@ public ClasspathElement newInstance(final Object classpathEntryObj, final LogNod * on exit, the set of all classpath elements * @param toplevelClasspathEltsOut * on exit, the toplevel classpath elements - * @param ClasspathEltOrder - * the toplevel classpath elt order * @return the work unit processor */ private WorkUnitProcessor newClasspathEntryWorkUnitProcessor( From 395b02cd608549c557a1136c7a0ed5f00a0d4aac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 May 2022 13:54:43 -0600 Subject: [PATCH 417/713] Revert missing plugin version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2ec341e59..ceeffee3b 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph classgraph-4.8.144 - + https://github.com/classgraph/classgraph/issues @@ -214,7 +214,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.6.12 org.apache.maven.plugins From aa0af0e8bf4e29fefa6bf074533fff266c38d566 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 May 2022 13:58:12 -0600 Subject: [PATCH 418/713] Solve JSON serialization exception (#682) --- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- src/main/java/io/github/classgraph/FieldInfo.java | 2 +- src/main/java/io/github/classgraph/MethodInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 5fd1809b5..092cc5fae 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -142,7 +142,7 @@ public class ClassInfo extends ScanResultObject implements Comparable AnnotationParameterValueList annotationDefaultParamValues; /** The type annotation decorators for the {@link ClassTypeSignature} instance. */ - List typeAnnotationDecorators; + transient List typeAnnotationDecorators; /** * Names of classes referenced by this class in class refs and type signatures in the constant pool of the diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index de161edbb..3680f64a4 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -78,7 +78,7 @@ public class FieldInfo extends ScanResultObject implements Comparable AnnotationInfoList annotationInfo; /** The type annotation decorators for the {@link TypeSignature} instance of this field. */ - private List typeAnnotationDecorators; + private transient List typeAnnotationDecorators; // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 8bb6f9e29..f91031691 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -104,7 +104,7 @@ public class MethodInfo extends ScanResultObject implements Comparable typeAnnotationDecorators; + private transient List typeAnnotationDecorators; private String[] thrownExceptionNames; From 9fbadf145c18e7682c0f4fda8c82a8d059eca214 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 May 2022 14:04:35 -0600 Subject: [PATCH 419/713] [maven-release-plugin] prepare release classgraph-4.8.147 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ceeffee3b..f60add960 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.147-SNAPSHOT + 4.8.147 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.147 From 7e5b4237d7a85e37140e1fe8b61b625b37b8bd55 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 May 2022 14:04:37 -0600 Subject: [PATCH 420/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f60add960..9da86ca4a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.147 + 4.8.148-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.147 + classgraph-4.8.144 From ce954c1c60c9167e30e332883e956ac50e9b53ba Mon Sep 17 00:00:00 2001 From: JJBRT <74912772+JJBRT@users.noreply.github.com> Date: Sun, 29 May 2022 09:34:23 +0200 Subject: [PATCH 421/713] Updating JVM-Driver Informations Updating informations regarding JDK versions supported by the JVM-Driver --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b186e8be0..66942c978 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ To use one of these libraries: 1. Add the [Narcissus](https://github.com/toolfactory/narcissus) library to your project as an extra dependency (this includes a native library, and only Linux x86/x64, Windows x86/x64, and Mac OS X x64 are currently supported -- feel free to contribute native code builds for other platforms or architectures). 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS;` before interacting with ClassGraph in any other way (this will load the Narcissus library as ClassGraph's reflection driver). * Or: - 1. Add the [JVM-Driver](https://github.com/toolfactory/jvm-driver) library to your project as an extra dependency (this uses only Java code, but only works to circumvent encapsulation without native code in JDK 16 currently). + 1. Add the [JVM-Driver](https://github.com/toolfactory/jvm-driver) library to your project as an extra dependency (this uses only Java code and works to bypass encapsulation without native code for all JDK versions between 8 and 18). 2. Set `ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER;` before interacting with ClassGraph in any other way (this will load the JVM-Driver library as ClassGraph's reflection driver). JDK 16's strong encapsulation is just the first step of trying to lock down Java's internals, so further restrictions are possible (e.g. it is likely that `setAccessible(true)` will fail in future JDK releases, even within a module, and probably the JNI API will be locked down soon, making Narcissus require a commandline flag to work). Therefore, **please convince your upstream runtime environment maintainers to expose the full classpath from their classloader using a public method or field, otherwise ClassGraph may stop working for your runtime environment in the future.** From 28a9dda0093c86b9bc99ccc45e660f42effe9865 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 02:09:04 +0000 Subject: [PATCH 422/713] Bump assertj-core from 3.22.0 to 3.23.1 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9da86ca4a..d42b30c0b 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ org.assertj assertj-core - 3.22.0 + 3.23.1 test From 0a711570cd32b4411567f6581bf5b0826fecdf4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 02:01:26 +0000 Subject: [PATCH 423/713] Bump maven-surefire-plugin from 3.0.0-M6 to 3.0.0-M7 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M6 to 3.0.0-M7. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M6...surefire-3.0.0-M7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9da86ca4a..bbe5fde7c 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M6 + 3.0.0-M7 org.codehaus.mojo From e4d98fef6ec0b91fdcfd821b97a996b84264259e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 02:02:32 +0000 Subject: [PATCH 424/713] Bump maven-enforcer-plugin from 3.0.0 to 3.1.0 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0...enforcer-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9da86ca4a..199c9963e 100644 --- a/pom.xml +++ b/pom.xml @@ -164,7 +164,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins From da5e4e80de3722c835fe0fcda487f81a0f424788 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 2 Jul 2022 20:47:29 -0600 Subject: [PATCH 425/713] Make MethodInfo.loadClassAndGetMethod work with type variables (#694) --- .../java/io/github/classgraph/MethodInfo.java | 22 +++++++++- .../classgraph/issues/issue694/Issue694.java | 44 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue694/Issue694.java diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index f91031691..6a75bab5d 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -820,7 +820,27 @@ private Class[] loadParameterClasses() { final List> parameterClasses = new ArrayList<>(allParameterInfo.length); for (final MethodParameterInfo mpi : allParameterInfo) { final TypeSignature parameterType = mpi.getTypeSignatureOrTypeDescriptor(); - parameterClasses.add(parameterType.loadClass()); + TypeSignature actualParameterType; + if (parameterType instanceof TypeVariableSignature) { + final TypeVariableSignature tvs = (TypeVariableSignature) parameterType; + final TypeParameter t = tvs.resolve(); + if (t.classBound != null) { + // Use class bound of type variable as concrete type, if available, + // in preference to using first interface bound (ignores interface + // bound(s), if present) + actualParameterType = t.classBound; + } else if (t.interfaceBounds != null && !t.interfaceBounds.isEmpty()) { + // Use first interface bound of type variable as concrete type + // (ignores 2nd and subsequent interface bound(s), if present) + actualParameterType = t.interfaceBounds.get(0); + } else { + // Sanity check, should not happen + throw new IllegalArgumentException("TypeVariableSignature has no bounds"); + } + } else { + actualParameterType = parameterType; + } + parameterClasses.add(actualParameterType.loadClass()); } return parameterClasses.toArray(new Class[0]); } diff --git a/src/test/java/io/github/classgraph/issues/issue694/Issue694.java b/src/test/java/io/github/classgraph/issues/issue694/Issue694.java new file mode 100644 index 000000000..34af653e1 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue694/Issue694.java @@ -0,0 +1,44 @@ +package io.github.classgraph.issues.issue694; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; + +public class Issue694 { + static class TestClass { + } + + public static > C test(final C collection) { + return collection; + } + + @Test + void methodWithParam() { + final ScanResult scan = new ClassGraph().acceptClasses(Issue694.class.getName()).enableAnnotationInfo() + .enableMethodInfo().scan(); + + final List foundMethods = new ArrayList<>(); + final List foundMethodInfo = new ArrayList<>(); + for (final ClassInfo info : scan.getAllStandardClasses()) { + for (final MethodInfo methodInfo : info.getDeclaredMethodInfo()) { + foundMethodInfo.add(methodInfo.toString()); + final Method method = methodInfo.loadClassAndGetMethod(); + foundMethods.add(method.toString()); + } + } + assertThat(foundMethodInfo).containsOnly( + "public static > C test(C collection)"); + assertThat(foundMethods).containsOnly( + "public static java.util.Collection io.github.classgraph.issues.issue694.Issue694.test(java.util.Collection)"); + } +} From 1f7ceef67f9e9c6eb02d5b44b66e02a5aa27df47 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Jul 2022 15:36:09 -0600 Subject: [PATCH 426/713] [maven-release-plugin] prepare release classgraph-4.8.148 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9da86ca4a..ac13355e4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.148-SNAPSHOT + 4.8.148 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.148 From c5d52442eb0225df5541e5ca4ce41e15302376b3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Jul 2022 15:36:12 -0600 Subject: [PATCH 427/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ac13355e4..62a573a70 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.148 + 4.8.149-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.148 + classgraph-4.8.144 From 5dc08f77ba7491ae9215dc07d8de2cd364398ab7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Jul 2022 22:24:23 -0600 Subject: [PATCH 428/713] Fix test --- .../java/io/github/classgraph/issues/issue694/Issue694.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue694/Issue694.java b/src/test/java/io/github/classgraph/issues/issue694/Issue694.java index 34af653e1..b9c1fe80c 100644 --- a/src/test/java/io/github/classgraph/issues/issue694/Issue694.java +++ b/src/test/java/io/github/classgraph/issues/issue694/Issue694.java @@ -37,7 +37,7 @@ void methodWithParam() { } } assertThat(foundMethodInfo).containsOnly( - "public static > C test(C collection)"); + "public static > C test(final C collection)"); assertThat(foundMethods).containsOnly( "public static java.util.Collection io.github.classgraph.issues.issue694.Issue694.test(java.util.Collection)"); } From 04457b9061424d8889003037302fc9a0bdc217c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Jul 2022 22:25:37 -0600 Subject: [PATCH 429/713] Parse out line numbers from method code attributes (#695). --- .../java/io/github/classgraph/Classfile.java | 28 ++++++++++++-- .../java/io/github/classgraph/MethodInfo.java | 38 ++++++++++++++++++- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index fd3f81546..3772af948 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1468,6 +1468,8 @@ private void readMethods() throws IOException, ClassfileFormatException { AnnotationInfo[][] methodParameterAnnotations = null; AnnotationInfoList methodAnnotationInfo = null; boolean methodHasBody = false; + int minLineNum = 0; + int maxLineNum = 0; if (!methodIsVisible || (!enableMethodInfo && !isAnnotation)) { // Skip method attributes for (int j = 0; j < attributesCount; j++) { @@ -1687,7 +1689,27 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { } } else if (constantPoolStringEquals(attributeNameCpIdx, "Code")) { methodHasBody = true; - reader.skip(attributeLength); + reader.skip(4); // max_stack, max_locals + final int codeLength = reader.readInt(); + reader.skip(codeLength); + final int exceptionTableLength = reader.readUnsignedShort(); + reader.skip(8 * exceptionTableLength); + final int codeAttrCount = reader.readUnsignedShort(); + for (int k = 0; k < codeAttrCount; k++) { + final int codeAttrCpIdx = reader.readUnsignedShort(); + final int codeAttrLen = reader.readInt(); + if (constantPoolStringEquals(codeAttrCpIdx, "LineNumberTable")) { + final int lineNumTableLen = reader.readUnsignedShort(); + for (int l = 0; l < lineNumTableLen; l++) { + reader.skip(2); // start_pc + final int lineNum = reader.readUnsignedShort(); + minLineNum = minLineNum == 0 ? lineNum : Math.min(minLineNum, lineNum); + maxLineNum = maxLineNum == 0 ? lineNum : Math.max(maxLineNum, lineNum); + } + } else { + reader.skip(codeAttrLen); + } + } } else { reader.skip(attributeLength); } @@ -1699,8 +1721,8 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { } methodInfoList.add(new MethodInfo(className, methodName, methodAnnotationInfo, methodModifierFlags, methodTypeDescriptor, methodTypeSignatureStr, methodParameterNames, - methodParameterModifiers, methodParameterAnnotations, methodHasBody, - methodTypeAnnotationDecorators, thrownExceptionNames)); + methodParameterModifiers, methodParameterAnnotations, methodHasBody, minLineNum, + maxLineNum, methodTypeAnnotationDecorators, thrownExceptionNames)); } } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 6a75bab5d..8fc12fe3d 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -103,6 +103,12 @@ public class MethodInfo extends ScanResultObject implements Comparable typeAnnotationDecorators; @@ -140,12 +146,20 @@ public class MethodInfo extends ScanResultObject implements Comparable methodTypeAnnotationDecorators, + final AnnotationInfo[][] parameterAnnotationInfo, final boolean hasBody, final int minLineNum, + final int maxLineNum, final List methodTypeAnnotationDecorators, final String[] thrownExceptionNames) { super(); this.declaringClassName = definingClassName; @@ -159,6 +173,8 @@ public class MethodInfo extends ScanResultObject implements Comparable Date: Tue, 5 Jul 2022 22:29:24 -0600 Subject: [PATCH 430/713] [maven-release-plugin] prepare release classgraph-4.8.149 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 62a573a70..14ca22cf9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.149-SNAPSHOT + 4.8.149 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.149 From e81f793faba16ed102aeba4760fa0b5cd6e260d2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Jul 2022 22:29:30 -0600 Subject: [PATCH 431/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 14ca22cf9..a754f500f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.149 + 4.8.150-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.149 + classgraph-4.8.144 From aa843294f647f14ddbf1d3b5b4246abaa01b8f05 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sun, 9 Oct 2022 16:16:33 +0200 Subject: [PATCH 432/713] Add ClassMemberInfo as super class of FieldInfo and MethodInfo --- .../io/github/classgraph/ClassMemberInfo.java | 371 ++++++++++++++++++ .../java/io/github/classgraph/FieldInfo.java | 264 +------------ .../java/io/github/classgraph/MethodInfo.java | 254 +----------- 3 files changed, 384 insertions(+), 505 deletions(-) create mode 100644 src/main/java/io/github/classgraph/ClassMemberInfo.java diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java new file mode 100644 index 000000000..9360a1991 --- /dev/null +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -0,0 +1,371 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Luke Hutchison + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE + * OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.github.classgraph; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.reflect.Modifier; + +import nonapi.io.github.classgraph.utils.Assert; + +/** + * Holds metadata about class members of a class encountered during a scan. + * All values are taken directly out of the classfile for the class. + */ +public abstract class ClassMemberInfo extends ScanResultObject implements HasName { + /** Defining class name. */ + protected String declaringClassName; + + /** The name of the class member. */ + protected String name; + + /** Method modifiers. */ + protected int modifiers; + + /** + * The JVM-internal type descriptor (missing type parameters, but including types for synthetic and mandated + * method parameters). + */ + protected String typeDescriptorStr; + + /** + * The type signature (may have type parameter information included, if present and available). + * Method parameter types are unaligned. + */ + protected String typeSignatureStr; + + /** The annotation on the class member, if any. */ + protected AnnotationInfoList annotationInfo; + + /** Default constructor for deserialization. */ + ClassMemberInfo() { + super(); + } + + /** + * Constructor. + * + * @param definingClassName + * The class the member is defined within. + * @param memberName + * The name of the class member. + * @param modifiers + * The field modifiers. + * @param typeDescriptorStr + * The field type descriptor. + * @param typeSignatureStr + * The field type signature. + * @param annotationInfo + * {@link AnnotationInfo} for any annotations on the field. + */ + public ClassMemberInfo(final String definingClassName, final String memberName, final int modifiers, + final String typeDescriptorStr, final String typeSignatureStr, final AnnotationInfoList annotationInfo) { + super(); + this.declaringClassName = definingClassName; + this.name = memberName; + this.modifiers = modifiers; + this.typeDescriptorStr = typeDescriptorStr; + this.typeSignatureStr = typeSignatureStr; + this.annotationInfo = annotationInfo == null || annotationInfo.isEmpty() ? null : annotationInfo; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the {@link ClassInfo} object for the class that declares this method. + * + * @return The {@link ClassInfo} object for the declaring class. + * + * @see #getClassName() + */ + @Override + public ClassInfo getClassInfo() { + return super.getClassInfo(); + } + + /** + * Get the name of the class that declares this member. + * + * @return The name of the declaring class. + * + * @see #getClassInfo() + */ + @Override + public String getClassName() { + return declaringClassName; + } + + /** + * Get the name of the field. + * + * @return The name of the field. + */ + @Override + public String getName() { + return name; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Returns the modifier bits for the method. + * + * @return The modifier bits for the method. + */ + public int getModifiers() { + return modifiers; + } + + /** + * Get the modifiers as a string, e.g. "public static final". For the modifier bits, call getModifiers(). + * + * @return The modifiers modifiers, as a string. + */ + public abstract String getModifiersStr(); + + + /** + * Returns true if this class member is public. + * + * @return True if the class member is public. + */ + public boolean isPublic() { + return Modifier.isPublic(modifiers); + } + + /** + * Returns true if this class member is private. + * + * @return True if the class member is private. + */ + public boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + /** + * Returns true if this class member is protected. + * + * @return True if the class member is protected. + */ + public boolean isProtected() { + return Modifier.isProtected(modifiers); + } + + /** + * Returns true if this class member is static. + * + * @return True if the class member is static. + */ + public boolean isStatic() { + return Modifier.isStatic(modifiers); + } + + /** + * Returns true if this class member is final. + * + * @return True if the class member is final. + */ + public boolean isFinal() { + return Modifier.isFinal(modifiers); + } + + /** + * Returns true if this class member is synthetic. + * + * @return True if the class member is synthetic. + */ + public boolean isSynthetic() { + return (modifiers & 0x1000) != 0; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Returns the parsed type descriptor for the class member, which will not include type parameters. + * If you need generic type parameters, call {@link #getTypeSignature()} instead. + * + * @return The parsed type descriptor string for the class member. + */ + public abstract HierarchicalTypeSignature getTypeDescriptor(); + + /** + * Returns the type descriptor string for the class member, which will not include type parameters. + * If you need generic type parameters, call {@link #getTypeSignatureStr()} instead. + * + * @return The type descriptor string for the class member. + */ + public String getTypeDescriptorStr() { + return typeDescriptorStr; + } + + /** + * Returns the parsed type signature for the class member, possibly including type parameters. + * If this returns null, that no type signature information is available for this class member, + * call {@link #getTypeDescriptor()} instead. + * + * @return The parsed type signature for the class member, or null if not available. + * @throws IllegalArgumentException + * if the class member type signature cannot be parsed (this should only be thrown in + * the case of classfile corruption, or a compiler bug that causes an invalid type + * signature to be written to the classfile). + */ + public abstract HierarchicalTypeSignature getTypeSignature(); + + /** + * Returns the type signature string for the class member, possibly including type parameters. + * If this returns null, indicating that no type signature information is available for this + * class member, call {@link #getTypeDescriptorStr()} instead. + * + * @return The type signature string for the class member, or null if not available. + */ + public String getTypeSignatureStr() { + return typeSignatureStr; + } + + /** + * Returns the type signature for the class member, possibly including type parameters. + * If the type signature is null, indicating that no type signature information is available + * for this class member, returns the type descriptor instead. + * + * @return The parsed type signature for the class member, or if not available, + * the parsed type descriptor for the class member. + */ + public abstract HierarchicalTypeSignature getTypeSignatureOrTypeDescriptor(); + + /** + * Returns the type signature string for the class member, possibly including type parameters. + * If the type signature string is null, indicating that no type signature information is available + * for this class member, returns the type descriptor string instead. + * + * @return The type signature string for the class member, or if not available, the type descriptor + * string for the class member. + */ + public String getTypeSignatureOrTypeDescriptorStr() { + if (typeSignatureStr != null) { + return typeSignatureStr; + } + return typeDescriptorStr; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get a list of annotations on this class member, along with any annotation parameter values, wrapped in + * {@link AnnotationInfo} objects. + * + * @return A list of annotations on this class member, along with any annotation parameter values, wrapped in + * {@link AnnotationInfo} objects, or the empty list if none. + */ + public AnnotationInfoList getAnnotationInfo() { + if (!scanResult.scanSpec.enableAnnotationInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); + } + return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST + : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); + } + + /** + * Get a the non-{@link Repeatable} annotation on this class member, or null if the class member does not + * have the annotation. + * (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this class member, + * or null if the class member does not have the annotation. + */ + public AnnotationInfo getAnnotationInfo(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfo(annotation.getName()); + } + + /** + * Get a the named non-{@link Repeatable} annotation on this class member, or null if the class member does + * not have the named annotation. + * (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) + * + * @param annotationName + * The annotation name. + * @return An {@link AnnotationInfo} object representing the named annotation on this class member, + * or null if the class member does not have the named annotation. + */ + public AnnotationInfo getAnnotationInfo(final String annotationName) { + return getAnnotationInfo().get(annotationName); + } + + /** + * Get a the {@link Repeatable} annotation on this class member, or the empty list if the class member does + * not have the annotation. + * + * @param annotation + * The annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this class member, + * or the empty list if the class member does not have the annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { + Assert.isAnnotation(annotation); + return getAnnotationInfoRepeatable(annotation.getName()); + } + + /** + * Get a the named {@link Repeatable} annotation on this class member, or the empty list if the class member + * does not have the named annotation. + * + * @param annotationName + * The annotation name. + * @return An {@link AnnotationInfoList} of all instances of the named annotation on this class member, + * or the empty list if the class member does not have the named annotation. + */ + public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationName) { + return getAnnotationInfo().getRepeatable(annotationName); + } + + /** + * Check if the class member has a given annotation. + * + * @param annotation + * The annotation. + * @return true if this class member has the annotation. + */ + public boolean hasAnnotation(final Class annotation) { + Assert.isAnnotation(annotation); + return hasAnnotation(annotation.getName()); + } + + /** + * Check if the class member has a given named annotation. + * + * @param annotationName + * The name of an annotation. + * @return true if this class member has the named annotation. + */ + public boolean hasAnnotation(final String annotationName) { + return getAnnotationInfo().containsName(annotationName); + } +} diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 3680f64a4..489be1a24 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -28,7 +28,6 @@ */ package io.github.classgraph; -import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -41,29 +40,13 @@ import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; -import nonapi.io.github.classgraph.utils.Assert; import nonapi.io.github.classgraph.utils.LogNode; /** * Holds metadata about fields of a class encountered during a scan. All values are taken directly out of the * classfile for the class. */ -public class FieldInfo extends ScanResultObject implements Comparable, HasName { - /** The declaring class name. */ - private String declaringClassName; - - /** The name of the field. */ - private String name; - - /** The modifiers. */ - private int modifiers; - - /** The type signature string. */ - private String typeSignatureStr; - - /** The type descriptor string. */ - private String typeDescriptorStr; - +public class FieldInfo extends ClassMemberInfo implements Comparable { /** The parsed type signature. */ private transient TypeSignature typeSignature; @@ -74,9 +57,6 @@ public class FieldInfo extends ScanResultObject implements Comparable // This is transient because the constant initializer value is final, so the value doesn't need to be serialized private ObjectTypedValueWrapper constantInitializerValue; - /** The annotation on the field, if any. */ - AnnotationInfoList annotationInfo; - /** The type annotation decorators for the {@link TypeSignature} instance of this field. */ private transient List typeAnnotationDecorators; @@ -108,48 +88,17 @@ public class FieldInfo extends ScanResultObject implements Comparable FieldInfo(final String definingClassName, final String fieldName, final int modifiers, final String typeDescriptorStr, final String typeSignatureStr, final Object constantInitializerValue, final AnnotationInfoList annotationInfo, final List typeAnnotationDecorators) { - super(); + super(definingClassName, fieldName, modifiers, typeDescriptorStr, typeSignatureStr, annotationInfo); if (fieldName == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("fieldName must not be null"); } - this.declaringClassName = definingClassName; - this.name = fieldName; - this.modifiers = modifiers; - this.typeDescriptorStr = typeDescriptorStr; - this.typeSignatureStr = typeSignatureStr; - this.constantInitializerValue = constantInitializerValue == null ? null : new ObjectTypedValueWrapper(constantInitializerValue); - this.annotationInfo = annotationInfo == null || annotationInfo.isEmpty() ? null : annotationInfo; this.typeAnnotationDecorators = typeAnnotationDecorators; } // ------------------------------------------------------------------------------------------------------------- - /** - * Get the name of the field. - * - * @return The name of the field. - */ - @Override - public String getName() { - return name; - } - - /** - * Get the {@link ClassInfo} object for the class that declares this field. - * - * @return The {@link ClassInfo} object for the declaring class. - * - * @see #getClassName() - */ - @Override - public ClassInfo getClassInfo() { - return super.getClassInfo(); - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Deprecated -- use {@link #getModifiersStr()} instead. * @@ -166,56 +115,13 @@ public String getModifierStr() { * * @return The field modifiers, as a string. */ + @Override public String getModifiersStr() { final StringBuilder buf = new StringBuilder(); TypeUtils.modifiersToString(modifiers, ModifierType.FIELD, /* ignored */ false, buf); return buf.toString(); } - /** - * Returns true if this field is public. - * - * @return True if the field is public. - */ - public boolean isPublic() { - return Modifier.isPublic(modifiers); - } - - /** - * Returns true if this field is private. - * - * @return True if the field is private. - */ - public boolean isPrivate() { - return Modifier.isPrivate(modifiers); - } - - /** - * Returns true if this field is protected. - * - * @return True if the field is protected. - */ - public boolean isProtected() { - return Modifier.isProtected(modifiers); - } - - /** - * Returns true if this field is static. - * - * @return True if the field is static. - */ - public boolean isStatic() { - return Modifier.isStatic(modifiers); - } - - /** - * Returns true if this field is final. - * - * @return True if the field is final. - */ - public boolean isFinal() { - return Modifier.isFinal(modifiers); - } /** * Returns true if this field is a transient field. @@ -226,15 +132,6 @@ public boolean isTransient() { return Modifier.isTransient(modifiers); } - /** - * Returns true if this field is synthetic. - * - * @return True if the field is synthetic. - */ - public boolean isSynthetic() { - return (modifiers & 0x1000) != 0; - } - /** * Returns true if this field is an enum constant. * @@ -244,21 +141,13 @@ public boolean isEnum() { return (modifiers & 0x4000) != 0; } - /** - * Returns the modifier bits for the field. - * - * @return The modifier bits. - */ - public int getModifiers() { - return modifiers; - } - /** * Returns the parsed type descriptor for the field, which will not include type parameters. If you need generic * type parameters, call {@link #getTypeSignature()} instead. * * @return The parsed type descriptor string for the field. */ + @Override public TypeSignature getTypeDescriptor() { if (typeDescriptorStr == null) { return null; @@ -279,16 +168,6 @@ public TypeSignature getTypeDescriptor() { return typeDescriptor; } - /** - * Returns the type descriptor string for the field, which will not include type parameters. If you need generic - * type parameters, call {@link #getTypeSignatureStr()} instead. - * - * @return The type descriptor string for the field. - */ - public String getTypeDescriptorStr() { - return typeDescriptorStr; - } - /** * Returns the parsed type signature for the field, possibly including type parameters. If this returns null, * indicating that no type signature information is available for this field, call {@link #getTypeDescriptor()} @@ -300,6 +179,7 @@ public String getTypeDescriptorStr() { * corruption, or a compiler bug that causes an invalid type signature to be written to the * classfile). */ + @Override public TypeSignature getTypeSignature() { if (typeSignatureStr == null) { return null; @@ -326,17 +206,6 @@ public TypeSignature getTypeSignature() { return typeSignature; } - /** - * Returns the type signature string for the field, possibly including type parameters. If this returns null, - * indicating that no type signature information is available for this field, call - * {@link #getTypeDescriptorStr()} instead. - * - * @return The type signature string for the field, or null if not available. - */ - public String getTypeSignatureStr() { - return typeSignatureStr; - } - /** * Returns the type signature for the field, possibly including type parameters. If the type signature is null, * indicating that no type signature information is available for this field, returns the type descriptor @@ -345,6 +214,7 @@ public String getTypeSignatureStr() { * @return The parsed type signature for the field, or if not available, the parsed type descriptor for the * field. */ + @Override public TypeSignature getTypeSignatureOrTypeDescriptor() { TypeSignature typeSig = null; try { @@ -358,22 +228,6 @@ public TypeSignature getTypeSignatureOrTypeDescriptor() { return getTypeDescriptor(); } - /** - * Returns the type signature string for the field, possibly including type parameters. If the type signature - * string is null, indicating that no type signature information is available for this field, returns the type - * descriptor string instead. - * - * @return The type signature string for the field, or if not available, the type descriptor string for the - * method. - */ - public String getTypeSignatureOrTypeDescriptorStr() { - if (typeSignatureStr != null) { - return typeSignatureStr; - } else { - return typeDescriptorStr; - } - } - /** * Returns the constant initializer value of a field. Requires * {@link ClassGraph#enableStaticFinalFieldConstantInitializerValues()} to have been called. Will only return @@ -393,98 +247,6 @@ public Object getConstantInitializerValue() { return constantInitializerValue == null ? null : constantInitializerValue.get(); } - /** - * Get a list of annotations on this field, along with any annotation parameter values, wrapped in - * {@link AnnotationInfo} objects. - * - * @return A list of annotations on this field, along with any annotation parameter values, wrapped in - * {@link AnnotationInfo} objects, or the empty list if none. - */ - public AnnotationInfoList getAnnotationInfo() { - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST - : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); - } - - /** - * Get a the non-{@link Repeatable} annotation on this field, or null if the field does not have the annotation. - * (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) - * - * @param annotation - * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this field, or null if the field does - * not have the annotation. - */ - public AnnotationInfo getAnnotationInfo(final Class annotation) { - Assert.isAnnotation(annotation); - return getAnnotationInfo(annotation.getName()); - } - - /** - * Get a the named non-{@link Repeatable} annotation on this field, or null if the field does not have the named - * annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) - * - * @param annotationName - * The annotation name. - * @return An {@link AnnotationInfo} object representing the named annotation on this field, or null if the - * field does not have the named annotation. - */ - public AnnotationInfo getAnnotationInfo(final String annotationName) { - return getAnnotationInfo().get(annotationName); - } - - /** - * Get a the {@link Repeatable} annotation on this field, or the empty list if the field does not have the - * annotation. - * - * @param annotation - * The annotation. - * @return An {@link AnnotationInfoList} of all instances of the annotation on this field, or the empty list if - * the field does not have the annotation. - */ - public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { - Assert.isAnnotation(annotation); - return getAnnotationInfoRepeatable(annotation.getName()); - } - - /** - * Get a the named {@link Repeatable} annotation on this field, or the empty list if the field does not have the - * named annotation. - * - * @param annotationName - * The annotation name. - * @return An {@link AnnotationInfoList} of all instances of the named annotation on this field, or the empty - * list if the field does not have the named annotation. - */ - public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationName) { - return getAnnotationInfo().getRepeatable(annotationName); - } - - /** - * Check if the field has a given annotation. - * - * @param annotation - * The annotation. - * @return true if this field has the annotation. - */ - public boolean hasAnnotation(final Class annotation) { - Assert.isAnnotation(annotation); - return hasAnnotation(annotation.getName()); - } - - /** - * Check if the field has a given named annotation. - * - * @param annotationName - * The name of an annotation. - * @return true if this field has the named annotation. - */ - public boolean hasAnnotation(final String annotationName) { - return getAnnotationInfo().containsName(annotationName); - } - // ------------------------------------------------------------------------------------------------------------- /** @@ -524,18 +286,6 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) // ------------------------------------------------------------------------------------------------------------- - /** - * Get the name of the class that declares this field. - * - * @return The name of the declaring class. - * - * @see #getClassInfo() - */ - @Override - public String getClassName() { - return declaringClassName; - } - /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#setScanResult(io.github.classgraph.ScanResult) */ diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 8fc12fe3d..9f264ec78 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -51,34 +51,10 @@ * Holds metadata about methods of a class encountered during a scan. All values are taken directly out of the * classfile for the class. */ -public class MethodInfo extends ScanResultObject implements Comparable, HasName { - /** Defining class name. */ - private String declaringClassName; - - /** Method name. */ - private String name; - - /** Method modifiers. */ - private int modifiers; - - /** Method annotations. */ - AnnotationInfoList annotationInfo; - - /** - * The JVM-internal type descriptor (missing type parameters, but including types for synthetic and mandated - * method parameters). - */ - private String typeDescriptorStr; - +public class MethodInfo extends ClassMemberInfo implements Comparable { /** The parsed type descriptor. */ private transient MethodTypeSignature typeDescriptor; - /** - * The type signature (may have type parameter information included, if present and available). Method parameter - * types are unaligned. - */ - private String typeSignatureStr; - /** The parsed type signature (or null if none). Method parameter types are unaligned. */ private transient MethodTypeSignature typeSignature; @@ -161,17 +137,10 @@ public class MethodInfo extends ScanResultObject implements Comparable methodTypeAnnotationDecorators, final String[] thrownExceptionNames) { - super(); - this.declaringClassName = definingClassName; - this.name = methodName; - this.modifiers = modifiers; - this.typeDescriptorStr = typeDescriptorStr; - this.typeSignatureStr = typeSignatureStr; + super(definingClassName, methodName, modifiers, typeDescriptorStr, typeSignatureStr, methodAnnotationInfo); this.parameterNames = parameterNames; this.parameterModifiers = parameterModifiers; this.parameterAnnotationInfo = parameterAnnotationInfo; - this.annotationInfo = methodAnnotationInfo == null || methodAnnotationInfo.isEmpty() ? null - : methodAnnotationInfo; this.hasBody = hasBody; this.minLineNum = minLineNum; this.maxLineNum = maxLineNum; @@ -192,45 +161,26 @@ public String getName() { return name; } - /** - * Returns the modifier bits for the method. - * - * @return The modifier bits for the method. - */ - public int getModifiers() { - return modifiers; - } - /** * Get the method modifiers as a String, e.g. "public static final". For the modifier bits, call * {@link #getModifiers()}. * * @return The modifiers for the method, as a String. */ + @Override public String getModifiersStr() { final StringBuilder buf = new StringBuilder(); TypeUtils.modifiersToString(modifiers, ModifierType.METHOD, isDefault(), buf); return buf.toString(); } - /** - * Get the {@link ClassInfo} object for the class that declares this method. - * - * @return The {@link ClassInfo} object for the declaring class. - * - * @see #getClassName() - */ - @Override - public ClassInfo getClassInfo() { - return super.getClassInfo(); - } - /** * Returns the parsed type descriptor for the method, which will not include type parameters. If you need * generic type parameters, call {@link #getTypeSignature()} instead. * * @return The parsed type descriptor for the method. */ + @Override public MethodTypeSignature getTypeDescriptor() { if (typeDescriptor == null) { try { @@ -287,16 +237,6 @@ public MethodTypeSignature getTypeDescriptor() { return typeDescriptor; } - /** - * Returns the type descriptor string for the method, which will not include type parameters. If you need - * generic type parameters, call {@link #getTypeSignatureStr()} instead. - * - * @return The type descriptor string for the method. - */ - public String getTypeDescriptorStr() { - return typeDescriptorStr; - } - /** * Returns the parsed type signature for the method, possibly including type parameters. If this returns null, * indicating that no type signature information is available for this method, call {@link #getTypeDescriptor()} @@ -308,6 +248,7 @@ public String getTypeDescriptorStr() { * classfile corruption, or a compiler bug that causes an invalid type signature to be written to * the classfile). */ + @Override public MethodTypeSignature getTypeSignature() { if (typeSignature == null && typeSignatureStr != null) { try { @@ -331,17 +272,6 @@ public MethodTypeSignature getTypeSignature() { return typeSignature; } - /** - * Returns the type signature string for the method, possibly including type parameters. If this returns null, - * indicating that no type signature information is available for this method, call - * {@link #getTypeDescriptorStr()} instead. - * - * @return The type signature string for the method, or null if not available. - */ - public String getTypeSignatureStr() { - return typeSignatureStr; - } - /** * Returns the parsed type signature for the method, possibly including type parameters. If the type signature * string is null, indicating that no type signature information is available for this method, returns the @@ -350,6 +280,7 @@ public String getTypeSignatureStr() { * @return The parsed type signature for the method, or if not available, the parsed type descriptor for the * method. */ + @Override public MethodTypeSignature getTypeSignatureOrTypeDescriptor() { MethodTypeSignature typeSig = null; try { @@ -363,22 +294,6 @@ public MethodTypeSignature getTypeSignatureOrTypeDescriptor() { return getTypeDescriptor(); } - /** - * Returns the type signature string for the method, possibly including type parameters. If the type signature - * string is null, indicating that no type signature information is available for this method, returns the type - * descriptor string instead. - * - * @return The type signature string for the method, or if not available, the type descriptor string for the - * method. - */ - public String getTypeSignatureOrTypeDescriptorStr() { - if (typeSignatureStr != null) { - return typeSignatureStr; - } else { - return typeDescriptorStr; - } - } - /** * Returns the list of exceptions thrown by the method, as a {@link ClassInfoList}. * @@ -420,51 +335,6 @@ public boolean isConstructor() { return "".equals(name); } - /** - * Returns true if this method is public. - * - * @return True if this method is public. - */ - public boolean isPublic() { - return Modifier.isPublic(modifiers); - } - - /** - * Returns true if this method is private. - * - * @return True if this method is private. - */ - public boolean isPrivate() { - return Modifier.isPrivate(modifiers); - } - - /** - * Returns true if this method is protected. - * - * @return True if this method is protected. - */ - public boolean isProtected() { - return Modifier.isProtected(modifiers); - } - - /** - * Returns true if this method is static. - * - * @return True if this method is static. - */ - public boolean isStatic() { - return Modifier.isStatic(modifiers); - } - - /** - * Returns true if this method is final. - * - * @return True if this method is final. - */ - public boolean isFinal() { - return Modifier.isFinal(modifiers); - } - /** * Returns true if this method is synchronized. * @@ -483,15 +353,6 @@ public boolean isBridge() { return (modifiers & 0x0040) != 0; } - /** - * Returns true if this method is synthetic. - * - * @return True if this is synthetic. - */ - public boolean isSynthetic() { - return (modifiers & 0x1000) != 0; - } - /** * Returns true if this method is a varargs method. * @@ -723,97 +584,6 @@ public MethodParameterInfo[] getParameterInfo() { // ------------------------------------------------------------------------------------------------------------- - /** - * Get a list of annotations on this method, along with any annotation parameter values. - * - * @return a list of annotations on this method, along with any annotation parameter values, wrapped in - * {@link AnnotationInfo} objects, or the empty list if none. - */ - public AnnotationInfoList getAnnotationInfo() { - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST - : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); - } - - /** - * Get a the non-{@link Repeatable} annotation on this method, or null if the method does not have the - * annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) - * - * @param annotation - * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this method, or null if the method - * does not have the annotation. - */ - public AnnotationInfo getAnnotationInfo(final Class annotation) { - Assert.isAnnotation(annotation); - return getAnnotationInfo(annotation.getName()); - } - - /** - * Get a the named non-{@link Repeatable} annotation on this method, or null if the method does not have the - * named annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) - * - * @param annotationName - * The annotation name. - * @return An {@link AnnotationInfo} object representing the named annotation on this method, or null if the - * method does not have the named annotation. - */ - public AnnotationInfo getAnnotationInfo(final String annotationName) { - return getAnnotationInfo().get(annotationName); - } - - /** - * Get a the {@link Repeatable} annotation on this method, or the empty list if the method does not have the - * annotation. - * - * @param annotation - * The annotation. - * @return An {@link AnnotationInfoList} containing all instances of the annotation on this method, or the empty - * list if the method does not have the annotation. - */ - public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { - Assert.isAnnotation(annotation); - return getAnnotationInfoRepeatable(annotation.getName()); - } - - /** - * Get a the named {@link Repeatable} annotation on this method, or the empty list if the method does not have - * the named annotation. - * - * @param annotationName - * The annotation name. - * @return An {@link AnnotationInfoList} containing all instances of the named annotation on this method, or the - * empty list if the method does not have the named annotation. - */ - public AnnotationInfoList getAnnotationInfoRepeatable(final String annotationName) { - return getAnnotationInfo().getRepeatable(annotationName); - } - - /** - * Check if this method has the annotation. - * - * @param annotation - * The annotation. - * @return true if this method has the annotation. - */ - public boolean hasAnnotation(final Class annotation) { - Assert.isAnnotation(annotation); - return hasAnnotation(annotation.getName()); - } - - /** - * Check if this method has the named annotation. - * - * @param annotationName - * The name of an annotation. - * @return true if this method has the named annotation. - */ - public boolean hasAnnotation(final String annotationName) { - return getAnnotationInfo().containsName(annotationName); - } - /** * Check if this method has a parameter with the annotation. * @@ -974,18 +744,6 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) // ------------------------------------------------------------------------------------------------------------- - /** - * Get the name of the class that declares this method. - * - * @return The name of the declaring class. - * - * @see #getClassInfo() - */ - @Override - public String getClassName() { - return declaringClassName; - } - /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#setScanResult(io.github.classgraph.ScanResult) */ From 830e36bf81f66f4e612073c536c35383816cf947 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 02:02:48 +0000 Subject: [PATCH 433/713] Bump jvm-driver from 8.9.6 to 9.4.2 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 8.9.6 to 9.4.2. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-8.9.6...jvm-driver-9.4.2) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a754f500f..916842f79 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 8.9.6 + 9.4.2 true From bd7305bc257423c62d719c828de1ccee00b823d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Nov 2022 02:00:33 +0000 Subject: [PATCH 434/713] Bump maven-release-plugin from 3.0.0-M5 to 3.0.0-M7 Bumps [maven-release-plugin](https://github.com/apache/maven-release) from 3.0.0-M5 to 3.0.0-M7. - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0-M5...maven-release-3.0.0-M7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a754f500f..f24dcac90 100644 --- a/pom.xml +++ b/pom.xml @@ -219,7 +219,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M5 + 3.0.0-M7 From 047e4651cef0acb65d1fcb66cb5ce0858a5461eb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 10 Nov 2022 23:42:17 -0700 Subject: [PATCH 435/713] Convert UNC paths to Path objects (#705) --- .../classgraph/classpath/ClasspathOrder.java | 16 ++++++++++++ .../classgraph/utils/FastPathResolver.java | 26 ++++++++----------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index ecbe58e11..f2b6cc614 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -432,6 +432,22 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla } return false; } + if (pathElementResolved.startsWith("//")) { + // Handle Windows UNC paths (#705) + try { + Path path = Paths.get(new URI(pathElementResolved)); + if (addClasspathEntry(path, pathElementResolved, classLoader, scanSpec)) { + if (log != null) { + log.log("Found classpath element: " + path + + (pathElementStr.equals(pathElementResolved) ? "" + : " -> " + pathElementResolved)); + } + return true; + } + } catch (Exception e) { + // Fall through + } + } if (addClasspathEntry(pathElementResolved, pathElementResolved, classLoader, scanSpec)) { if (log != null) { log.log("Found classpath element: " + pathElementStr diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 4d0853e14..74ca9e887 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -266,30 +266,26 @@ public static String resolve(final String resolveBasePath, final String relative } while (matchedPrefix); if (isFileOrJarURL) { - if (WINDOWS) { - if (relativePath.startsWith("\\\\\\\\", startIdx) || relativePath.startsWith("////", startIdx)) { - // Windows UNC URL - startIdx += 4; - prefix += "//"; - isAbsolutePath = true; - } else { - if (relativePath.startsWith("\\\\", startIdx)) { - startIdx += 2; - } - } - } - // "file:///" or "jar:///" URL if (relativePath.startsWith("///", startIdx)) { + // "file:///" or "jar:///" URL startIdx += 2; + } else if (relativePath.startsWith("//", startIdx)) { + // "file://" or "jar://" URL + startIdx += 1; } } // Handle Windows paths starting with a drive designation as an absolute path if (WINDOWS) { - if ((relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx))) { + if ((relativePath.startsWith("////", startIdx) || relativePath.startsWith("\\\\\\\\", startIdx))) { + // Windows UNC path + startIdx += 4; + prefix += "//"; + isAbsolutePath = true; + } else if ((relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx))) { // Windows UNC path startIdx += 2; - prefix = "//"; + prefix += "//"; isAbsolutePath = true; } else if (relativePath.length() - startIdx > 2 && Character.isLetter(relativePath.charAt(startIdx)) && relativePath.charAt(startIdx + 1) == ':') { From 58a8a75b78238ae2e682ce8cde3a8547f03eb91e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:43:18 +0000 Subject: [PATCH 436/713] Bump maven-javadoc-plugin from 3.4.0 to 3.4.1 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.0...maven-javadoc-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..7542e90d7 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.0 + 3.4.1 org.apache.maven.plugins From 5672603056f88030b3532843a29be70dc53fb270 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:43:21 +0000 Subject: [PATCH 437/713] Bump junit-jupiter from 5.8.2 to 5.9.1 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..fde8c026e 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ org.junit.jupiter junit-jupiter - 5.8.2 + 5.9.1 test From b6cbc3f7549a3d947f1a7c404a824993a038faca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:43:37 +0000 Subject: [PATCH 438/713] Bump slf4j-api from 2.0.0-alpha7 to 2.0.3 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0-alpha7 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha7...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..5c54a93ad 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.0-alpha7 + 2.0.3 test From 5de4a0f79c12c2f4ac4c2bec428efffb01bd92b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:43:45 +0000 Subject: [PATCH 439/713] Bump org.eclipse.jdt.annotation from 2.2.600 to 2.2.700 Bumps [org.eclipse.jdt.annotation](https://github.com/eclipse-jdt/eclipse.jdt.core) from 2.2.600 to 2.2.700. - [Release notes](https://github.com/eclipse-jdt/eclipse.jdt.core/releases) - [Commits](https://github.com/eclipse-jdt/eclipse.jdt.core/commits) --- updated-dependencies: - dependency-name: org.eclipse.jdt:org.eclipse.jdt.annotation dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..f5126f682 100644 --- a/pom.xml +++ b/pom.xml @@ -150,7 +150,7 @@ org.eclipse.jdt org.eclipse.jdt.annotation - 2.2.600 + 2.2.700 provided From 3c4225c90fb4dd3d5bcd38955ba421c2e9c94328 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:44:00 +0000 Subject: [PATCH 440/713] Bump maven-resources-plugin from 3.2.0 to 3.3.0 Bumps [maven-resources-plugin](https://github.com/apache/maven-resources-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.2.0...maven-resources-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..97f3eb1f4 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.0 org.apache.maven.plugins From 5c3904cb2f9962fd5f0089cf57d00414329d1084 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 06:44:03 +0000 Subject: [PATCH 441/713] Bump slf4j-jdk14 from 2.0.0-alpha7 to 2.0.3 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha7 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha7...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f24dcac90..1d72f8f99 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.0-alpha7 + 2.0.3 test From 49157500037590fd4435277af32eea08bf7f9188 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Nov 2022 00:48:00 -0700 Subject: [PATCH 442/713] Code cleanup --- .../io/github/classgraph/ClassMemberInfo.java | 103 +++++++++--------- .../classgraph/classpath/ClasspathOrder.java | 4 +- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java index 9360a1991..e0b60a38b 100644 --- a/src/main/java/io/github/classgraph/ClassMemberInfo.java +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -35,8 +35,8 @@ import nonapi.io.github.classgraph.utils.Assert; /** - * Holds metadata about class members of a class encountered during a scan. - * All values are taken directly out of the classfile for the class. + * Holds metadata about class members of a class encountered during a scan. All values are taken directly out of the + * classfile for the class. */ public abstract class ClassMemberInfo extends ScanResultObject implements HasName { /** Defining class name. */ @@ -55,8 +55,8 @@ public abstract class ClassMemberInfo extends ScanResultObject implements HasNam protected String typeDescriptorStr; /** - * The type signature (may have type parameter information included, if present and available). - * Method parameter types are unaligned. + * The type signature (may have type parameter information included, if present and available). Method parameter + * types are unaligned. */ protected String typeSignatureStr; @@ -85,14 +85,15 @@ public abstract class ClassMemberInfo extends ScanResultObject implements HasNam * {@link AnnotationInfo} for any annotations on the field. */ public ClassMemberInfo(final String definingClassName, final String memberName, final int modifiers, - final String typeDescriptorStr, final String typeSignatureStr, final AnnotationInfoList annotationInfo) { - super(); - this.declaringClassName = definingClassName; - this.name = memberName; - this.modifiers = modifiers; - this.typeDescriptorStr = typeDescriptorStr; - this.typeSignatureStr = typeSignatureStr; - this.annotationInfo = annotationInfo == null || annotationInfo.isEmpty() ? null : annotationInfo; + final String typeDescriptorStr, final String typeSignatureStr, + final AnnotationInfoList annotationInfo) { + super(); + this.declaringClassName = definingClassName; + this.name = memberName; + this.modifiers = modifiers; + this.typeDescriptorStr = typeDescriptorStr; + this.typeSignatureStr = typeSignatureStr; + this.annotationInfo = annotationInfo == null || annotationInfo.isEmpty() ? null : annotationInfo; } // ------------------------------------------------------------------------------------------------------------- @@ -149,7 +150,6 @@ public int getModifiers() { */ public abstract String getModifiersStr(); - /** * Returns true if this class member is public. * @@ -207,16 +207,16 @@ public boolean isSynthetic() { // ------------------------------------------------------------------------------------------------------------- /** - * Returns the parsed type descriptor for the class member, which will not include type parameters. - * If you need generic type parameters, call {@link #getTypeSignature()} instead. + * Returns the parsed type descriptor for the class member, which will not include type parameters. If you need + * generic type parameters, call {@link #getTypeSignature()} instead. * * @return The parsed type descriptor string for the class member. */ public abstract HierarchicalTypeSignature getTypeDescriptor(); /** - * Returns the type descriptor string for the class member, which will not include type parameters. - * If you need generic type parameters, call {@link #getTypeSignatureStr()} instead. + * Returns the type descriptor string for the class member, which will not include type parameters. If you need + * generic type parameters, call {@link #getTypeSignatureStr()} instead. * * @return The type descriptor string for the class member. */ @@ -225,22 +225,22 @@ public String getTypeDescriptorStr() { } /** - * Returns the parsed type signature for the class member, possibly including type parameters. - * If this returns null, that no type signature information is available for this class member, - * call {@link #getTypeDescriptor()} instead. + * Returns the parsed type signature for the class member, possibly including type parameters. If this returns + * null, that no type signature information is available for this class member, call + * {@link #getTypeDescriptor()} instead. * * @return The parsed type signature for the class member, or null if not available. * @throws IllegalArgumentException - * if the class member type signature cannot be parsed (this should only be thrown in - * the case of classfile corruption, or a compiler bug that causes an invalid type - * signature to be written to the classfile). + * if the class member type signature cannot be parsed (this should only be thrown in the case of + * classfile corruption, or a compiler bug that causes an invalid type signature to be written to + * the classfile). */ public abstract HierarchicalTypeSignature getTypeSignature(); /** - * Returns the type signature string for the class member, possibly including type parameters. - * If this returns null, indicating that no type signature information is available for this - * class member, call {@link #getTypeDescriptorStr()} instead. + * Returns the type signature string for the class member, possibly including type parameters. If this returns + * null, indicating that no type signature information is available for this class member, call + * {@link #getTypeDescriptorStr()} instead. * * @return The type signature string for the class member, or null if not available. */ @@ -249,22 +249,22 @@ public String getTypeSignatureStr() { } /** - * Returns the type signature for the class member, possibly including type parameters. - * If the type signature is null, indicating that no type signature information is available - * for this class member, returns the type descriptor instead. + * Returns the type signature for the class member, possibly including type parameters. If the type signature is + * null, indicating that no type signature information is available for this class member, returns the type + * descriptor instead. * - * @return The parsed type signature for the class member, or if not available, - * the parsed type descriptor for the class member. + * @return The parsed type signature for the class member, or if not available, the parsed type descriptor for + * the class member. */ public abstract HierarchicalTypeSignature getTypeSignatureOrTypeDescriptor(); /** - * Returns the type signature string for the class member, possibly including type parameters. - * If the type signature string is null, indicating that no type signature information is available - * for this class member, returns the type descriptor string instead. + * Returns the type signature string for the class member, possibly including type parameters. If the type + * signature string is null, indicating that no type signature information is available for this class member, + * returns the type descriptor string instead. * - * @return The type signature string for the class member, or if not available, the type descriptor - * string for the class member. + * @return The type signature string for the class member, or if not available, the type descriptor string for + * the class member. */ public String getTypeSignatureOrTypeDescriptorStr() { if (typeSignatureStr != null) { @@ -291,14 +291,13 @@ public AnnotationInfoList getAnnotationInfo() { } /** - * Get a the non-{@link Repeatable} annotation on this class member, or null if the class member does not - * have the annotation. - * (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) + * Get a the non-{@link Repeatable} annotation on this class member, or null if the class member does not have + * the annotation. (Use {@link #getAnnotationInfoRepeatable(Class)} for {@link Repeatable} annotations.) * * @param annotation * The annotation. - * @return An {@link AnnotationInfo} object representing the annotation on this class member, - * or null if the class member does not have the annotation. + * @return An {@link AnnotationInfo} object representing the annotation on this class member, or null if the + * class member does not have the annotation. */ public AnnotationInfo getAnnotationInfo(final Class annotation) { Assert.isAnnotation(annotation); @@ -306,27 +305,27 @@ public AnnotationInfo getAnnotationInfo(final Class annota } /** - * Get a the named non-{@link Repeatable} annotation on this class member, or null if the class member does - * not have the named annotation. - * (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} annotations.) + * Get a the named non-{@link Repeatable} annotation on this class member, or null if the class member does not + * have the named annotation. (Use {@link #getAnnotationInfoRepeatable(String)} for {@link Repeatable} + * annotations.) * * @param annotationName * The annotation name. - * @return An {@link AnnotationInfo} object representing the named annotation on this class member, - * or null if the class member does not have the named annotation. + * @return An {@link AnnotationInfo} object representing the named annotation on this class member, or null if + * the class member does not have the named annotation. */ public AnnotationInfo getAnnotationInfo(final String annotationName) { return getAnnotationInfo().get(annotationName); } /** - * Get a the {@link Repeatable} annotation on this class member, or the empty list if the class member does - * not have the annotation. + * Get a the {@link Repeatable} annotation on this class member, or the empty list if the class member does not + * have the annotation. * * @param annotation * The annotation. - * @return An {@link AnnotationInfoList} of all instances of the annotation on this class member, - * or the empty list if the class member does not have the annotation. + * @return An {@link AnnotationInfoList} of all instances of the annotation on this class member, or the empty + * list if the class member does not have the annotation. */ public AnnotationInfoList getAnnotationInfoRepeatable(final Class annotation) { Assert.isAnnotation(annotation); @@ -339,8 +338,8 @@ public AnnotationInfoList getAnnotationInfoRepeatable(final Class Date: Fri, 11 Nov 2022 00:48:32 -0700 Subject: [PATCH 443/713] Update classpath --- .classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.classpath b/.classpath index 1fb85a3c1..5576b4361 100644 --- a/.classpath +++ b/.classpath @@ -31,7 +31,7 @@ - + From 395b6cde8bb1b64ca8dd2cd89615770c3d10ff76 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Nov 2022 00:49:11 -0700 Subject: [PATCH 444/713] Always return a TypeParameter from resolve() (#706) --- .../java/io/github/classgraph/FieldInfo.java | 1 - .../io/github/classgraph/TypeParameter.java | 2 +- .../classgraph/TypeVariableSignature.java | 60 +++++++++++++------ .../classgraph/issues/issue706/Issue706.java | 31 ++++++++++ 4 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue706/Issue706.java diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 489be1a24..6722d06d3 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -122,7 +122,6 @@ public String getModifiersStr() { return buf.toString(); } - /** * Returns true if this field is a transient field. * diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 095bc5559..e05936234 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -62,7 +62,7 @@ public final class TypeParameter extends HierarchicalTypeSignature { * @param interfaceBounds * The type parameter interface bound. */ - private TypeParameter(final String identifier, final ReferenceTypeSignature classBound, + protected TypeParameter(final String identifier, final ReferenceTypeSignature classBound, final List interfaceBounds) { super(); this.name = identifier; diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index d8a46a4ee..4b7717b6a 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; @@ -49,6 +50,9 @@ public final class TypeVariableSignature extends ClassRefOrTypeVariableSignature /** The method signature that this type variable is part of. */ MethodTypeSignature containingMethodSignature; + /** The resolved type parameter, if any. */ + private TypeParameter typeParameterCached; + // ------------------------------------------------------------------------------------------------------------- /** @@ -87,36 +91,47 @@ public String getName() { * method or the enclosing class. */ public TypeParameter resolve() { + if (typeParameterCached != null) { + return typeParameterCached; + } // Try resolving the type variable against the containing method if (containingMethodSignature != null && containingMethodSignature.typeParameters != null && !containingMethodSignature.typeParameters.isEmpty()) { for (final TypeParameter typeParameter : containingMethodSignature.typeParameters) { if (typeParameter.name.equals(this.name)) { + typeParameterCached = typeParameter; return typeParameter; } } } // If that failed, try resolving the type variable against the containing class - final ClassInfo containingClassInfo = getClassInfo(); - if (containingClassInfo == null) { - throw new IllegalArgumentException("Could not find ClassInfo object for " + definingClassName); - } - ClassTypeSignature containingClassSignature = null; - try { - containingClassSignature = containingClassInfo.getTypeSignature(); - } catch (final Exception e) { - // Ignore - } - if (containingClassSignature != null && containingClassSignature.typeParameters != null - && !containingClassSignature.typeParameters.isEmpty()) { - for (final TypeParameter typeParameter : containingClassSignature.typeParameters) { - if (typeParameter.name.equals(this.name)) { - return typeParameter; + if (getClassName() != null) { + final ClassInfo containingClassInfo = getClassInfo(); + if (containingClassInfo == null) { + throw new IllegalArgumentException("Could not find ClassInfo object for " + definingClassName); + } + ClassTypeSignature containingClassSignature = null; + try { + containingClassSignature = containingClassInfo.getTypeSignature(); + } catch (final Exception e) { + // Ignore + } + if (containingClassSignature != null && containingClassSignature.typeParameters != null + && !containingClassSignature.typeParameters.isEmpty()) { + for (final TypeParameter typeParameter : containingClassSignature.typeParameters) { + if (typeParameter.name.equals(this.name)) { + typeParameterCached = typeParameter; + return typeParameter; + } } } } - throw new IllegalArgumentException( - "Could not resolve " + name + " against parameters of the defining method or enclosing class"); + // If that failed, then this is a type variable that cannot be resolved. + // Return a new TypeParameter that only has the name set, with no class or interface bounds. (#706) + final TypeParameter typeParameter = new TypeParameter(name, null, Collections.emptyList()); + typeParameter.setScanResult(scanResult); + typeParameterCached = typeParameter; + return typeParameter; } // ------------------------------------------------------------------------------------------------------------- @@ -191,7 +206,16 @@ protected String getClassName() { */ @Override protected void findReferencedClassNames(final Set refdClassNames) { - // No class names present in type variables + // Any class names present in resolved type variables have to be present in enclosing method or class, + // so there's no need to look up class references in resolved type variables + } + + @Override + void setScanResult(final ScanResult scanResult) { + super.setScanResult(scanResult); + if (typeParameterCached != null) { + typeParameterCached.setScanResult(scanResult); + } } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/test/java/io/github/classgraph/issues/issue706/Issue706.java b/src/test/java/io/github/classgraph/issues/issue706/Issue706.java new file mode 100644 index 000000000..b090323dc --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue706/Issue706.java @@ -0,0 +1,31 @@ +package io.github.classgraph.issues.issue706; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeArgument; +import io.github.classgraph.TypeVariableSignature; + +public class Issue706 { + static public class GenericBase { + } + + static public class GenericBypass extends GenericBase { + } + + @Test + void genericSuperclass() { + final ScanResult scanResult = new ClassGraph().acceptPackages(Issue706.class.getPackage().getName()) + .enableClassInfo().scan(); + final ClassInfo bypassCls = scanResult.getClassInfo(GenericBypass.class.getName()); + final TypeArgument superclassArg = bypassCls.getTypeSignature().getSuperclassSignature() + .getSuffixTypeArguments().get(0).get(0); + final TypeVariableSignature superclassArgTVar = (TypeVariableSignature) superclassArg.getTypeSignature(); + final Object bypassTParamFromSuperclassArg = superclassArgTVar.resolve(); + assertThat(bypassTParamFromSuperclassArg.toString()).isEqualTo("T"); + } +} From 28ec7238c59f8d1fac7dcb7b238d54966d37d74f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Nov 2022 00:59:31 -0700 Subject: [PATCH 445/713] Add `isPackageVisible` (#702) --- src/main/java/io/github/classgraph/ClassInfo.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 092cc5fae..6eff09acb 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1213,6 +1213,15 @@ public boolean isProtected() { return Modifier.isProtected(modifiers); } + /** + * Checks if the class has default (package) visibility. + * + * @return true if this class is only visible within its package. + */ + public boolean isPackageVisible() { + return !isPublic() && !isPrivate() && !isProtected(); + } + /** * Checks if the class is abstract. * From 0380d4653b46709accfb0cb1634399e49220508b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Nov 2022 01:37:07 -0700 Subject: [PATCH 446/713] Add unit test (#694) --- .../classgraph/issues/issue696/Issue696.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue696/Issue696.java diff --git a/src/test/java/io/github/classgraph/issues/issue696/Issue696.java b/src/test/java/io/github/classgraph/issues/issue696/Issue696.java new file mode 100644 index 000000000..a295df078 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue696/Issue696.java @@ -0,0 +1,48 @@ +package io.github.classgraph.issues.issue696; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodParameterInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.issues.issue696.Issue696.BrokenAnnotation.Dynamic; + +public class Issue696 { + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + public static @interface Foo { + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + public static @interface Bar { + } + + public static class BrokenAnnotation { + public class Dynamic { + public Dynamic(@Foo String param1, @Bar String param2) { + } + } + } + + @Test + void genericSuperclass() { + final ScanResult scanResult = new ClassGraph().acceptPackages(Issue696.class.getPackage().getName()) + .enableMethodInfo().enableAnnotationInfo().scan(); + final ClassInfo dynamic = scanResult.getClassInfo(Dynamic.class.getName()); + final MethodParameterInfo[] paramInfo = dynamic.getConstructorInfo().get(0).getParameterInfo(); + // Inner classes have an initial "mandated" param + assertThat(paramInfo.length).isEqualTo(3); + assertThat(paramInfo[0].getAnnotationInfo()).isEmpty(); + assertThat(paramInfo[1].getAnnotationInfo().get(0).getName()).isEqualTo(Foo.class.getName()); + assertThat(paramInfo[2].getAnnotationInfo().get(0).getName()).isEqualTo(Bar.class.getName()); + } +} From 3607c41a6d9b15843dc8ef227696b494d038dc53 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 11 Nov 2022 01:38:01 -0700 Subject: [PATCH 447/713] Fix for old JDK --- src/main/java/io/github/classgraph/TypeVariableSignature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 4b7717b6a..0f1cfdd8c 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -128,7 +128,8 @@ public TypeParameter resolve() { } // If that failed, then this is a type variable that cannot be resolved. // Return a new TypeParameter that only has the name set, with no class or interface bounds. (#706) - final TypeParameter typeParameter = new TypeParameter(name, null, Collections.emptyList()); + final TypeParameter typeParameter = new TypeParameter(name, null, + Collections. emptyList()); typeParameter.setScanResult(scanResult); typeParameterCached = typeParameter; return typeParameter; From 207d81b77ccb90184d5efac18d945de585a77841 Mon Sep 17 00:00:00 2001 From: LGTM Migrator Date: Fri, 11 Nov 2022 10:26:58 +0000 Subject: [PATCH 448/713] Add CodeQL workflow for GitHub code scanning --- .github/workflows/codeql.yml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..6408ddfdd --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: "CodeQL" + +on: + push: + branches: [ "latest" ] + pull_request: + branches: [ "latest" ] + schedule: + - cron: "47 11 * * 6" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ java ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" From 8335f5469e0a39ce66a0000ae26c3f3f356e0f15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 02:00:51 +0000 Subject: [PATCH 449/713] Bump maven-site-plugin from 3.12.0 to 3.12.1 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.12.0...maven-site-plugin-3.12.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-site-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..cbe2a9219 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ org.apache.maven.plugins maven-site-plugin - 3.12.0 + 3.12.1 From 96ba062ab5301659244b04654f150e4c9aeb0ac0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 02:00:54 +0000 Subject: [PATCH 450/713] Bump maven-deploy-plugin from 3.0.0-M2 to 3.0.0 Bumps [maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.0.0-M2 to 3.0.0. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.0.0-M2...maven-deploy-plugin-3.0.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..6b5098db1 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0-M2 + 3.0.0 org.apache.maven.plugins From 19ac6664e160cf9afa42fcbc6531f2b3533105b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 02:01:01 +0000 Subject: [PATCH 451/713] Bump animal-sniffer-enforcer-rule from 1.21 to 1.22 Bumps [animal-sniffer-enforcer-rule](https://github.com/mojohaus/animal-sniffer) from 1.21 to 1.22. - [Release notes](https://github.com/mojohaus/animal-sniffer/releases) - [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.21...animal-sniffer-parent-1.22) --- updated-dependencies: - dependency-name: org.codehaus.mojo:animal-sniffer-enforcer-rule dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..e31215e17 100644 --- a/pom.xml +++ b/pom.xml @@ -255,7 +255,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.21 + 1.22 From 2ab130208689df24ed767d03ca358832f8b3f95b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 02:01:09 +0000 Subject: [PATCH 452/713] Bump maven-jar-plugin from 3.2.2 to 3.3.0 Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.2 to 3.3.0. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.2...maven-jar-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..7b2d8b78d 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.2 + 3.3.0 org.apache.maven.plugins From f5e620057068b4a012424a3afad99cc22dae557a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 02:01:16 +0000 Subject: [PATCH 453/713] Bump maven-install-plugin from 3.0.0-M1 to 3.0.1 Bumps [maven-install-plugin](https://github.com/apache/maven-install-plugin) from 3.0.0-M1 to 3.0.1. - [Release notes](https://github.com/apache/maven-install-plugin/releases) - [Commits](https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.0.0-M1...maven-install-plugin-3.0.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-install-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..ff78a2d03 100644 --- a/pom.xml +++ b/pom.xml @@ -236,7 +236,7 @@ org.apache.maven.plugins maven-install-plugin - 3.0.0-M1 + 3.0.1 org.apache.maven.plugins From 9165a0f04a31a9f54b9523af8f65da64d42a1857 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Nov 2022 22:46:54 -0700 Subject: [PATCH 454/713] Improve UNC path handling (#705) --- .../classgraph/classpath/ClasspathOrder.java | 17 +++++++++++++---- .../classgraph/utils/FastPathResolver.java | 7 +------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index c802ec37b..dc94a3096 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -433,16 +433,25 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } if (pathElementResolved.startsWith("//")) { - // Handle Windows UNC paths (#705) + // Handle Windows UNC paths (#705). + // File supports UNC paths directly: + // https://wiki.eclipse.org/Eclipse/UNC_Paths#Programming_with_UNC_paths try { - final Path path = Paths.get(new URI(pathElementResolved)); - if (addClasspathEntry(path, pathElementResolved, classLoader, scanSpec)) { + final File file = new File(pathElementResolved); + if (addClasspathEntry(file, pathElementResolved, classLoader, scanSpec)) { if (log != null) { - log.log("Found classpath element: " + path + log.log("Found classpath element: " + file + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); } return true; + } else { + if (log != null) { + log.log("Ignoring duplicate classpath element: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" + : " -> " + pathElementResolved)); + } + return false; } } catch (final Exception e) { // Fall through diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 74ca9e887..59c84c7ad 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -277,12 +277,7 @@ public static String resolve(final String resolveBasePath, final String relative // Handle Windows paths starting with a drive designation as an absolute path if (WINDOWS) { - if ((relativePath.startsWith("////", startIdx) || relativePath.startsWith("\\\\\\\\", startIdx))) { - // Windows UNC path - startIdx += 4; - prefix += "//"; - isAbsolutePath = true; - } else if ((relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx))) { + if (relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx)) { // Windows UNC path startIdx += 2; prefix += "//"; From ac1a2823c0cabf5143332f586dfd1bdbf6a1e8ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Nov 2022 23:28:59 -0700 Subject: [PATCH 455/713] Do not stop scanning dir hierarchy on first error --- .../github/classgraph/ClasspathElement.java | 7 ++++--- .../classgraph/ClasspathElementDir.java | 20 +++++-------------- .../classgraph/ClasspathElementModule.java | 5 ++--- .../classgraph/ClasspathElementZip.java | 8 +++++--- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 55c5feef2..ebb91f010 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -171,16 +171,16 @@ int getNumClassfileMatches() { * the relative path * @param log * the log + * @return true if path should be scanned */ - protected void checkResourcePathAcceptReject(final String relativePath, final LogNode log) { + protected boolean checkResourcePathAcceptReject(final String relativePath, final LogNode log) { // Accept/reject classpath elements based on file resource paths if (!scanSpec.classpathElementResourcePathAcceptReject.acceptAndRejectAreEmpty()) { if (scanSpec.classpathElementResourcePathAcceptReject.isRejected(relativePath)) { if (log != null) { log.log("Reached rejected classpath element resource path, stopping scanning: " + relativePath); } - skipClasspathElement = true; - return; + return false; } if (scanSpec.classpathElementResourcePathAcceptReject.isSpecificallyAccepted(relativePath)) { if (log != null) { @@ -189,6 +189,7 @@ protected void checkResourcePathAcceptReject(final String relativePath, final Lo containsSpecificallyAcceptedClasspathElementResourcePath = true; } } + return true; } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index ec9b9fb0f..9a9c6a3e8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -312,9 +312,6 @@ Resource getResource(final String relativePath) { * the log */ private void scanPathRecursively(final Path path, final LogNode log) { - if (skipClasspathElement) { - return; - } // See if this canonical path has been scanned before, so that recursive scanning doesn't get stuck in an // infinite loop due to symlinks Path canonicalPath; @@ -362,8 +359,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { } // Accept/reject classpath elements based on dir resource paths - checkResourcePathAcceptReject(dirRelativePathStr, log); - if (skipClasspathElement) { + if (!checkResourcePathAcceptReject(dirRelativePathStr, log)) { return; } @@ -396,7 +392,6 @@ private void scanPathRecursively(final Path path, final LogNode log) { if (log != null) { log.log("Could not read directory " + path + " : " + e.getMessage()); } - skipClasspathElement = true; return; } Collections.sort(pathsInDir); @@ -420,8 +415,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { } // Accept/reject classpath elements based on file resource paths - checkResourcePathAcceptReject(subPathRelativeStr, subLog); - if (skipClasspathElement) { + if (!checkResourcePathAcceptReject(subPathRelativeStr, subLog)) { return; } @@ -467,13 +461,6 @@ private void scanPathRecursively(final Path path, final LogNode log) { try { if (Files.isDirectory(subPath)) { scanPathRecursively(subPath, subLog); - // If a rejected classpath element resource path was found, it will set skipClasspathElement - if (skipClasspathElement) { - if (subLog != null) { - subLog.addElapsedTime(); - } - return; - } } } catch (final SecurityException e) { if (subLog != null) { @@ -503,6 +490,9 @@ private void scanPathRecursively(final Path path, final LogNode log) { */ @Override void scanPaths(final LogNode log) { + if (!checkResourcePathAcceptReject(classpathEltPath.toString(), log)) { + skipClasspathElement = true; + } if (skipClasspathElement) { return; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 70d197fd8..5ab2b4f49 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -346,9 +346,8 @@ void scanPaths(final LogNode log) { } // Accept/reject classpath elements based on file resource paths - checkResourcePathAcceptReject(relativePath, log); - if (skipClasspathElement) { - return; + if (!checkResourcePathAcceptReject(relativePath, log)) { + continue; } // Get match status of the parent directory of this resource's relative path (or reuse the last diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index d31621512..c45b9b0ec 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -474,6 +474,9 @@ void scanPaths(final LogNode log) { if (logicalZipFile == null) { skipClasspathElement = true; } + if (!checkResourcePathAcceptReject(getZipFilePath(), log)) { + skipClasspathElement = true; + } if (skipClasspathElement) { return; } @@ -582,9 +585,8 @@ void scanPaths(final LogNode log) { } // Accept/reject classpath elements based on file resource paths - checkResourcePathAcceptReject(relativePath, log); - if (skipClasspathElement) { - return; + if (!checkResourcePathAcceptReject(relativePath, log)) { + continue; } // Get match status of the parent directory of this ZipEntry file's relative path (or reuse the last From 71c56916e4511c64d38dac7274c7fd1a4b401fde Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Nov 2022 23:32:33 -0700 Subject: [PATCH 456/713] [maven-release-plugin] prepare release classgraph-4.8.150 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ce0ff2a5a..9fdced481 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.150-SNAPSHOT + 4.8.150 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.144 + classgraph-4.8.150 From da7bf213ee7a71b4ba88d363865836f9bf8b1a1d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Nov 2022 23:33:14 -0700 Subject: [PATCH 457/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9fdced481..022a33e48 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.150 + 4.8.151-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 4fb65ec2b10f0f02c22589f142cf8c98750913ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 02:01:07 +0000 Subject: [PATCH 458/713] Bump jmh-core from 1.35 to 1.36 Bumps [jmh-core](https://github.com/openjdk/jmh) from 1.35 to 1.36. - [Release notes](https://github.com/openjdk/jmh/releases) - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.36) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e7d3a1441..1149824e6 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ org.openjdk.jmh jmh-core - 1.35 + 1.36 test From a85ee180cabf8a203867cd03141add2193f4657e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 02:01:10 +0000 Subject: [PATCH 459/713] Bump jmh-generator-annprocess from 1.35 to 1.36 Bumps [jmh-generator-annprocess](https://github.com/openjdk/jmh) from 1.35 to 1.36. - [Release notes](https://github.com/openjdk/jmh/releases) - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.36) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e7d3a1441..afbcf9fa6 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.35 + 1.36 test From 4ad23400b4ff590d2f7fbb1449d2b282c321311c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 16 Nov 2022 17:42:15 -0700 Subject: [PATCH 460/713] More UNC path fixes (#705) --- src/main/java/io/github/classgraph/Scanner.java | 10 +++++++--- .../classgraph/classpath/ClasspathOrder.java | 4 ++-- .../github/classgraph/utils/FastPathResolver.java | 15 --------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 142e5aebd..0457e9ddb 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -459,9 +459,13 @@ private static Object normalizeClasspathEntry(final Object classpathEntryObj) th // Last-ditch effort -- try to convert String to Path if (classpathEntryObjNormalized instanceof String) { try { - classpathEntryObjNormalized = Paths.get((String) classpathEntryObjNormalized); - } catch (final InvalidPathException e) { - throw new IOException("Malformed path: " + classpathEntryObj + " : " + e); + classpathEntryObjNormalized = new File((String) classpathEntryObjNormalized).toPath(); + } catch (final Exception e) { + try { + classpathEntryObjNormalized = Paths.get((String) classpathEntryObjNormalized); + } catch (final InvalidPathException e2) { + throw new IOException("Malformed path: " + classpathEntryObj + " : " + e2); + } } } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index dc94a3096..14c99ab28 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -314,7 +314,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla } pathElementURL = null; } - if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof File + if (pathElementURL != null || pathElement instanceof URI || pathElement instanceof File || pathElement instanceof Path) { if (!filter(pathElementURL, pathElementStr)) { if (log != null) { @@ -326,7 +326,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // for URI and Path objects, convert to URL; for File objects, use the toString result (the path) final Object classpathElementObj; classpathElementObj = pathElement instanceof File ? pathElementStr - : pathElement instanceof Path || pathElement instanceof URI ? pathElementURL : pathElement; + : pathElementURL != null ? pathElementURL : pathElement; if (addClasspathEntry(classpathElementObj, pathElementStr, classLoader, scanSpec)) { if (log != null) { log.log("Found classpath element: " + pathElementStr); diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 59c84c7ad..53ec286d6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -234,11 +234,6 @@ public static String resolve(final String resolveBasePath, final String relative // Strip off "file:" prefix from relative path matchedPrefix = true; startIdx += 5; - while (startIdx < relativePath.length() - 1 && relativePath.charAt(startIdx) == '/' - && relativePath.charAt(startIdx + 1) == '/') { - // Strip off all but one '/' after "file:" - startIdx++; - } isFileOrJarURL = true; } else { // Preserve the number of slashes on custom URL schemes (#420) @@ -265,16 +260,6 @@ public static String resolve(final String resolveBasePath, final String relative } } while (matchedPrefix); - if (isFileOrJarURL) { - if (relativePath.startsWith("///", startIdx)) { - // "file:///" or "jar:///" URL - startIdx += 2; - } else if (relativePath.startsWith("//", startIdx)) { - // "file://" or "jar://" URL - startIdx += 1; - } - } - // Handle Windows paths starting with a drive designation as an absolute path if (WINDOWS) { if (relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx)) { From 64b005a9705036ece9f065fec3587655cb336a74 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Nov 2022 00:19:34 -0700 Subject: [PATCH 461/713] Fix some regressions --- .../classgraph/classpath/ClasspathOrder.java | 51 +++++++++++++++---- .../issues/issue171/Issue171Test.java | 2 +- .../issues/issue46/Issue46Test.java | 2 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 14c99ab28..d13d33538 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -43,6 +43,8 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import io.github.classgraph.ClassGraph.ClasspathElementFilter; import io.github.classgraph.ClassGraph.ClasspathElementURLFilter; @@ -67,6 +69,9 @@ public class ClasspathOrder { /** Suffixes for automatic package roots, e.g. "!/BOOT-INF/classes". */ private static final List AUTOMATIC_PACKAGE_ROOT_SUFFIXES = new ArrayList<>(); + /** Match URL schemes. */ + private static final Pattern schemeMatcher = Pattern.compile("^[a-zA-Z+\\-.]+:"); + static { for (final String prefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { AUTOMATIC_PACKAGE_ROOT_SUFFIXES.add("!/" + prefix.substring(0, prefix.length() - 1)); @@ -228,7 +233,14 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle // For File, just use path string : pathElementStrWithoutSuffix; } catch (MalformedURLException | URISyntaxException | InvalidPathException e) { - return false; + try { + pathElementWithoutSuffix = pathElement instanceof URL + ? new URL("file:" + pathElementStrWithoutSuffix) + : pathElement instanceof URI ? new URI("file:" + pathElementStrWithoutSuffix) + : pathElementStrWithoutSuffix; + } catch (MalformedURLException | URISyntaxException | InvalidPathException e2) { + return false; + } } } // Deduplicate classpath elements @@ -295,22 +307,39 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla : pathElement instanceof Path ? ((Path) pathElement).toUri().toURL() : pathElement instanceof File ? ((File) pathElement).toURI().toURL() : null; if (pathElementURL == null) { - // Fallback -- call toString() on the path element, then try converting to a URL via File + // Fallback -- call toString() on the path element, then try converting to a URL final String pathElementToStr = pathElement.toString(); + final boolean hasJarScheme = pathElementToStr.startsWith("jar:"); + int startIdx = hasJarScheme ? 4 : 0; + final Matcher m1 = schemeMatcher.matcher(pathElementToStr.substring(startIdx)); + String scheme = ""; + if (m1.find()) { + scheme = m1.group(); + startIdx += scheme.length(); + } + final String urlStr = (pathElementToStr.contains("!/") || hasJarScheme ? "jar:" : "") + + (scheme.isEmpty() ? "file:" : scheme) + // Escape '%' characters (#255) + + pathElementToStr.substring(startIdx).replace("%", "%25"); try { - pathElementURL = new File(pathElementToStr).toURI().toURL(); - } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e) { - if (log != null) { - log.log("Failed to convert classpath element to URL, " - + "Try prepending \"file:\" to create a URL (" + e + "): " + pathElementStr); + pathElementURL = new URL(urlStr); + } catch (final MalformedURLException e) { + try { + pathElementURL = new File(pathElementToStr).toURI().toURL(); + } catch (final MalformedURLException | IllegalArgumentException | IOError + | SecurityException e1) { + if (log != null) { + log.log("Failed to convert classpath element to URL, " + + "Try prepending \"file:\" to create a URL (" + e1 + "): " + pathElementStr); + } + // Final fallback -- try just using the raw string as a URL + pathElementURL = new URL(pathElementToStr); } - // Final fallback -- try prepending "file:" to create a URL - pathElementURL = new URL("file:" + pathElementToStr); } } - } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e1) { + } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e2) { if (log != null) { - log.log("Cannot convert to URL (" + e1 + "): " + pathElement); + log.log("Cannot convert to URL (" + e2 + "): " + pathElement); } pathElementURL = null; } diff --git a/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java b/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java index 683c04d41..c801beea7 100644 --- a/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java +++ b/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java @@ -23,7 +23,7 @@ public void springBootFullyExecutableJar() { try (ScanResult scanResult = new ClassGraph() .acceptPackagesNonRecursive("hello", "org.springframework.boot") - .overrideClasspath(jarURL + "!BOOT-INF/classes") // + .overrideClasspath("jar:" + jarURL + "!/BOOT-INF/classes") // .scan()) { final List classNames = scanResult.getAllClasses().getNames(); assertThat(classNames).contains("hello.HelloController", diff --git a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java index a76cb62fa..3b9f79483 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -46,7 +46,7 @@ public class Issue46Test { public void issue46Test() { final String jarPath = "jar:file://" + Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() - + "!level2.jar!level3.jar!classpath1/classpath2"; + + "!/level2.jar!/level3.jar!/classpath1/classpath2"; try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } From a2186364263be3c97a646d36f38f114f191739ed Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Nov 2022 00:31:56 -0700 Subject: [PATCH 462/713] Another UNC fix (#705) --- .../io/github/classgraph/classpath/ClasspathOrder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index d13d33538..d08755390 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -293,6 +293,11 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped pathElementStr = pathElement instanceof Path ? ((Path) pathElement).toUri().toString() : pathElement.toString(); + // Windows paths ("C:\x\y") are encoded as "file:///C:/x/y" by Path.toUri().toString(), + // but then Paths.get() can't handle paths of the form "///C:/x/y" + if (pathElementStr.startsWith("file:///")) { + pathElementStr = ((Path) pathElement).toFile().toString(); + } } catch (IOError | SecurityException e) { pathElementStr = pathElement.toString(); } From a1ec04444624fe0c3b9e2d1a79b34f554f551bf1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Nov 2022 00:38:43 -0700 Subject: [PATCH 463/713] [maven-release-plugin] prepare release classgraph-4.8.151 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d88ecfa3a..e552370f7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.151-SNAPSHOT + 4.8.151 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.151 From 3333c24b979622d7da4a01b558634d5433b22825 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Nov 2022 00:38:46 -0700 Subject: [PATCH 464/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e552370f7..d61cbac2e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.151 + 4.8.152-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.151 + classgraph-4.8.150 From e1761feda1a4b6e1cd9c934b79ed9f063d2655c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 02:00:29 +0000 Subject: [PATCH 465/713] Bump maven-install-plugin from 3.0.1 to 3.1.0 Bumps [maven-install-plugin](https://github.com/apache/maven-install-plugin) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/apache/maven-install-plugin/releases) - [Commits](https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.0.1...maven-install-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-install-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d61cbac2e..6d5b56644 100644 --- a/pom.xml +++ b/pom.xml @@ -236,7 +236,7 @@ org.apache.maven.plugins maven-install-plugin - 3.0.1 + 3.1.0 org.apache.maven.plugins From 3bc3b5a813f0203165a19679435c8207dfe2d0d3 Mon Sep 17 00:00:00 2001 From: Sebastian Thomschke Date: Mon, 21 Nov 2022 11:58:27 +0100 Subject: [PATCH 466/713] Update javadoc Replace usage of nouns "field", "method" with "class member" --- .../io/github/classgraph/ClassMemberInfo.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java index e0b60a38b..1b2acee8a 100644 --- a/src/main/java/io/github/classgraph/ClassMemberInfo.java +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -45,18 +45,18 @@ public abstract class ClassMemberInfo extends ScanResultObject implements HasNam /** The name of the class member. */ protected String name; - /** Method modifiers. */ + /** Class member modifiers. */ protected int modifiers; /** * The JVM-internal type descriptor (missing type parameters, but including types for synthetic and mandated - * method parameters). + * class member parameters). */ protected String typeDescriptorStr; /** - * The type signature (may have type parameter information included, if present and available). Method parameter - * types are unaligned. + * The type signature (may have type parameter information included, if present and available). Class member + * parameter types are unaligned. */ protected String typeSignatureStr; @@ -76,13 +76,13 @@ public abstract class ClassMemberInfo extends ScanResultObject implements HasNam * @param memberName * The name of the class member. * @param modifiers - * The field modifiers. + * The class member modifiers. * @param typeDescriptorStr - * The field type descriptor. + * The class member type descriptor. * @param typeSignatureStr - * The field type signature. + * The class member type signature. * @param annotationInfo - * {@link AnnotationInfo} for any annotations on the field. + * {@link AnnotationInfo} for any annotations on the class member. */ public ClassMemberInfo(final String definingClassName, final String memberName, final int modifiers, final String typeDescriptorStr, final String typeSignatureStr, @@ -99,7 +99,7 @@ public ClassMemberInfo(final String definingClassName, final String memberName, // ------------------------------------------------------------------------------------------------------------- /** - * Get the {@link ClassInfo} object for the class that declares this method. + * Get the {@link ClassInfo} object for the class that declares this class member. * * @return The {@link ClassInfo} object for the declaring class. * @@ -123,9 +123,9 @@ public String getClassName() { } /** - * Get the name of the field. + * Get the name of the class member. * - * @return The name of the field. + * @return The name of the class member. */ @Override public String getName() { @@ -135,9 +135,9 @@ public String getName() { // ------------------------------------------------------------------------------------------------------------- /** - * Returns the modifier bits for the method. + * Returns the modifier bits for the class member. * - * @return The modifier bits for the method. + * @return The modifier bits for the class member. */ public int getModifiers() { return modifiers; From 448a7745c327d9ef0ce48ce5e31d3b3fe63c29c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 02:00:34 +0000 Subject: [PATCH 467/713] Bump slf4j-jdk14 from 2.0.3 to 2.0.5 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.3 to 2.0.5. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.5) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d61cbac2e..5cd064951 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.3 + 2.0.5 test From 17f864d3e273e94f383352e0ac5658a1ed8c2d7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 02:00:40 +0000 Subject: [PATCH 468/713] Bump slf4j-api from 2.0.3 to 2.0.5 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.3 to 2.0.5. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.5) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d61cbac2e..b2bc2c66b 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.3 + 2.0.5 test From a7711bd7ffd9cad4652e814a9608392d30b9b4c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 10 Dec 2022 00:27:16 -0700 Subject: [PATCH 469/713] Fix for UNC paths (#736) --- .../nonapi/io/github/classgraph/utils/FileUtils.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 9f4bfeca4..07aa37e06 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -178,7 +178,8 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } // Handle "..", "." and empty path segments, if any were found - final boolean pathHasInitialSlash = pathLen > 0 && pathChars[0] == '/'; + final boolean pathHasInitialSlash = pathChars[0] == '/'; + final boolean pathHasInitialSlashSlash = pathHasInitialSlash && pathLen > 1 && pathChars[1] == '/'; final StringBuilder pathSanitized = new StringBuilder(pathLen + 16); if (foundSegmentToSanitize) { // Sanitize between "!" section markers separately (".." should not apply past preceding "!") @@ -230,7 +231,13 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } else { pathSanitized.append(path); } - + + // Intended to preserve the double slash at the start of UNC paths (#736). + // e.g. //server/file/path + if (pathHasInitialSlashSlash) { + pathSanitized.insert(0, '/'); + } + int startIdx = 0; if (removeInitialSlash || !pathHasInitialSlash) { // Strip off leading "/" if it needs to be removed, or if it wasn't present in the original path From a434fb46e11f1e198a3649f08a112d16961650e3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 10 Dec 2022 00:30:01 -0700 Subject: [PATCH 470/713] Have to remove zero bugs commitment at this point --- README.md | 2 +- Zero-Bugs-Commitment.md | 77 ----------------------------------------- 2 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 Zero-Bugs-Commitment.md diff --git a/README.md b/README.md index 66942c978..7f48fb220 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ClassGraph is an uber-fast parallelized classpath scanner and module scanner for
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) -| ClassGraph is now fully stable. This project adheres to the **[Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md)**. | +| ClassGraph is stable and mature, and has a low bug report rate, despite being used by hundreds of projects. | |-----------------------------| ### ClassGraph vs. Java Introspection diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md deleted file mode 100644 index 399949ea3..000000000 --- a/Zero-Bugs-Commitment.md +++ /dev/null @@ -1,77 +0,0 @@ -# The Zero Bugs Commitment - -This project adheres to the **Zero Bugs Commitment** (`#ZeroBugs`). -This is a commitment to practice *responsible software engineering*, -*proactive community participation*, and *positive community engagement*, -with a goal of keeping the count of known or open bugs at zero, while -respecting and cultivating contributions. - -**As developers of this project, we pledge that:** - -## (1) We will prioritize fixing bugs over implementing new features. - -*(Motivation: It is human nature to be much more interested in building new -things than doing the hard work to fix old, broken things.)* - -💡 We pledge, wherever reasonable, to prioritize fixing known bugs above -implementing new features, with the goal of **keeping the count of known or -open bugs at zero**. - -## (2) We will take responsibility for code we have written or contributed to. - -*(Motivation: As attention shifts between projects, it is difficult to return -to work on old code. This can lead to bit rot.)* - -💡 We pledge to take long-term responsibility for any significant code we -create or contribute to, fixing problems and updating code as necessary to -prevent bit rot. If we can no longer fulfill this responsibility, we will -find someone else who can assume the responsibility for our code. - -Note that this is about taking *personal responsibility* for our own work, not -about who has *official maintainership* for a project or piece of code. - -## (3) We will be responsive during the bugfixing process. - -*(Motivation: It is easy to delay responding to a bug report, a bug comment or -a request until the issue becomes forgotten or obsolete. This is the unfortunate -end state of a significant proportion of bug reports filed across the open -source ecosystem.)* - -💡 We pledge to be responsive to bug reports, comments and requests currently -open in our project's bug tracker, and to be proactive in resolving problems -as quickly as practical. - -## (4) We will be respectful and inclusive. - -*(Motivation: Open source communities have been known to reject halting but -earnest efforts of new contributors. Bug trackers are also full of pet -complaints and bugs closed as `#WONTFIX`, without a significant attempt to -understand core issues, or to find a solution or compromise.)* - -💡 We pledge to cultivate contributions and growth in our community by -welcoming, encouraging, and helping users who offer contributions; -by striving to listen to the needs and requests of community members; -and by trying to find a reasonable solution or middle ground when there is -a disagreement. - ---- - -**THIS DOCUMENT IS IN THE PUBLIC DOMAIN** - -You are strongly encouraged to share this commitment, to make this commitment -yourself for software that you develop or maintain, and to encourage others to -do the same. - -**To sign this pledge**, you can add the following wording to your project -homepage, linking to this document, or to your own copy or your own version of -this document: - -| **This project adheres to the [Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** | -|-----------------------------| - -You may modify or redistribute this document at will without restriction. -However, please leave a record of your changes below. - -#### Version history: - -0.1: Original version (author: Luke Hutchison) From ac12360ac1010fd8a8ba7deb6c23cc539244f6ae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 10 Dec 2022 00:30:47 -0700 Subject: [PATCH 471/713] [maven-release-plugin] prepare release classgraph-4.8.152 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9fce8d2a7..de2af84a8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.152-SNAPSHOT + 4.8.152 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.152 From cae560f4d1248d96b3ef09818995573494705d2b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 10 Dec 2022 00:30:50 -0700 Subject: [PATCH 472/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index de2af84a8..83537c779 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.152 + 4.8.153-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.152 + classgraph-4.8.150 From 14f1260bc2f812b9b88e805b2787d6a7c43a40a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 02:00:29 +0000 Subject: [PATCH 473/713] Bump slf4j-api from 2.0.5 to 2.0.6 Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.5 to 2.0.6. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.5...v_2.0.6) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83537c779..5ed282ded 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.5 + 2.0.6 test From 8efca4b5fbd167ee614790006cb81c0f8ce5b8e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 02:00:35 +0000 Subject: [PATCH 474/713] Bump slf4j-jdk14 from 2.0.5 to 2.0.6 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.5 to 2.0.6. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.5...v_2.0.6) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83537c779..ad7a16303 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.5 + 2.0.6 test From 139171ff30ab9b8b066087a78f1e3178bcff1fb7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Dec 2022 03:11:06 -0700 Subject: [PATCH 475/713] Test for #735 --- .../classgraph/issues/issue735/Issue735.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue735/Issue735.java diff --git a/src/test/java/io/github/classgraph/issues/issue735/Issue735.java b/src/test/java/io/github/classgraph/issues/issue735/Issue735.java new file mode 100644 index 000000000..23430538a --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue735/Issue735.java @@ -0,0 +1,37 @@ +package io.github.classgraph.issues.issue735; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +public class Issue735 { + static interface Base { + T get(); + } + + static class Derived1 implements Base { + public String get() { + return null; + } + } + + static abstract class Derived2 implements Base { + } + + @Test + void genericSuperclass() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue735.class.getPackage().getName()) + .enableAllInfo().ignoreClassVisibility().ignoreMethodVisibility().scan()) { + ClassInfo ci1 = scanResult.getClassInfo(Derived1.class.getName()); + assertThat(ci1.getMethodInfo().get(0).getTypeSignatureOrTypeDescriptor().getResultType().toString()) + .isEqualTo(String.class.getName()); + ClassInfo ci2 = scanResult.getClassInfo(Derived2.class.getName()); + assertThat(ci2.getMethodInfo().get(0).getTypeSignatureOrTypeDescriptor().getResultType().toString()) + .isEqualTo("T"); + } + } +} From a0c2b41ad6ea8c7234a600465ea2296b7ddd68a5 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:52:17 +0100 Subject: [PATCH 476/713] Add `ClassInfo#getSourceFile` --- .../java/io/github/classgraph/ClassInfo.java | 23 +++++++++++++++++++ .../java/io/github/classgraph/Classfile.java | 6 +++++ 2 files changed, 29 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 6eff09acb..3b47efcc5 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -95,6 +95,9 @@ public class ClassInfo extends ScanResultObject implements Comparable /** The synthetic class type descriptor. */ private transient ClassTypeSignature typeDescriptor; + /** The name of the source file this class has been compiled from */ + private String sourceFile; + /** The fully-qualified defining method name, for anonymous inner classes. */ private String fullyQualifiedDefiningMethodName; @@ -457,6 +460,16 @@ void setIsRecord(final boolean isRecord) { } } + /** + * Set source file. + * + * @param sourceFile + * the source file + */ + void setSourceFile(final String sourceFile) { + this.sourceFile = sourceFile; + } + /** * Add {@link ClassTypeAnnotationDecorator} instances. * @@ -2919,6 +2932,16 @@ public ClassTypeSignature getTypeDescriptor() { return typeDescriptor; } + /** + * Returns the name of the source file this class has been compiled from, + * such as {@code ClassInfo.java} or {@code KClass.kt} + * + * @return The name of the source file of this class + */ + public String getSourceFile() { + return sourceFile; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 3772af948..55956af8d 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -131,6 +131,9 @@ class Classfile { /** The type signature. */ private String typeSignatureStr; + /** The source file, such as Classfile.java */ + private String sourceFile; + /** The type annotation decorators for the {@link ClassTypeSignature} instance. */ private List classTypeAnnotationDecorators; @@ -487,6 +490,7 @@ void link(final Map classNameToClassInfo, classInfo.setIsInterface(isInterface); classInfo.setIsAnnotation(isAnnotation); classInfo.setIsRecord(isRecord); + classInfo.setSourceFile(sourceFile); if (superclassName != null) { classInfo.addSuperclass(superclassName, classNameToClassInfo); } @@ -1877,6 +1881,8 @@ public void decorate(final ClassTypeSignature classTypeSignature) { } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Get class type signature, including type variables typeSignatureStr = getConstantPoolString(reader.readUnsignedShort()); + } else if (constantPoolStringEquals(attributeNameCpIdx, "SourceFile")) { + sourceFile = getConstantPoolString(reader.readUnsignedShort()); } else if (constantPoolStringEquals(attributeNameCpIdx, "EnclosingMethod")) { final String innermostEnclosingClassName = getConstantPoolClassName(reader.readUnsignedShort()); final int enclosingMethodCpIdx = reader.readUnsignedShort(); From 66f0ed98224848f243812313581e3f1643b72969 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:54:52 +0100 Subject: [PATCH 477/713] Specify nullability --- src/main/java/io/github/classgraph/ClassInfo.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 3b47efcc5..cb8e50d1e 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2934,9 +2934,11 @@ public ClassTypeSignature getTypeDescriptor() { /** * Returns the name of the source file this class has been compiled from, - * such as {@code ClassInfo.java} or {@code KClass.kt} + * such as {@code ClassInfo.java} or {@code KClass.kt}. * - * @return The name of the source file of this class + *

This field may be {@code null}. + * + * @return The name of the source file of this class, or {@code null} if not available */ public String getSourceFile() { return sourceFile; From 90ef1d20a40d2d7c3e48520662c8d88b54207bfa Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Tue, 20 Dec 2022 15:45:18 +0200 Subject: [PATCH 478/713] Add `getTypeAnnotationInfo` to `TypeParameter` and `TypeArgument` Fixes classgraph/classgraph#741 Exposes missing API for accessing parsed annotations on type parameters and wildcard type arguments. --- .../io/github/classgraph/TypeArgument.java | 9 ++++ .../io/github/classgraph/TypeParameter.java | 9 ++++ .../issue741/TypeArgumentAnnotationTest.java | 53 +++++++++++++++++++ .../issue741/TypeParameterAnnotationTest.java | 51 ++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java create mode 100644 src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 302d57034..8ddacb1f5 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -225,6 +225,15 @@ public void findReferencedClassNames(final Set refdClassNames) { } } + /** + * Get a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or null if none. + * + * @return a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or null if none. + */ + public AnnotationInfoList getTypeAnnotationInfo() { + return typeAnnotationInfo; + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index e05936234..420bd911a 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -97,6 +97,15 @@ public List getInterfaceBounds() { return interfaceBounds; } + /** + * Get a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if none. + * + * @return a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if none. + */ + public AnnotationInfoList getTypeAnnotationInfo() { + return typeAnnotationInfo; + } + @Override protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { if (typePath.isEmpty()) { diff --git a/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java new file mode 100644 index 000000000..90f8e6c97 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java @@ -0,0 +1,53 @@ +package io.github.classgraph.issues.issue741; + +import io.github.classgraph.*; +import org.junit.jupiter.api.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TypeArgumentAnnotationTest { + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface A { + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface B { + String value(); + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface C { + Class t(); + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface D { + int n(); + } + + static class U { + } + + void setValueList(List<@A @B("foo") @C(t=U.class) @D(n=50) ?> valueList) { + } + + @Test + void typeArgumentAnnotation() { + try (final ScanResult scanResult = new ClassGraph().acceptPackages(TypeArgumentAnnotationTest.class.getPackage().getName()) + .enableAllInfo().scan()) { + final ClassInfo cls = scanResult.getClassInfo(TypeArgumentAnnotationTest.class.getName()); + final MethodInfo method = cls.getMethodInfo().get("setValueList").get(0); + final MethodParameterInfo parameterInfo = method.getParameterInfo()[0]; + final TypeArgument typeArgument = ((ClassRefTypeSignature) parameterInfo.getTypeSignatureOrTypeDescriptor()).getTypeArguments().get(0); + final AnnotationInfoList annotationInfoList = typeArgument.getTypeAnnotationInfo(); + assertThat(annotationInfoList.get(0).toStringWithSimpleNames()).isEqualTo("@A"); + assertThat(annotationInfoList.get(1).toStringWithSimpleNames()).isEqualTo("@B(\"foo\")"); + assertThat(annotationInfoList.get(2).toStringWithSimpleNames()).isEqualTo("@C(t=U.class)"); + assertThat(annotationInfoList.get(3).toStringWithSimpleNames()).isEqualTo("@D(n=50)"); + } + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java new file mode 100644 index 000000000..a67df1886 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java @@ -0,0 +1,51 @@ +package io.github.classgraph.issues.issue741; + +import io.github.classgraph.*; +import org.junit.jupiter.api.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TypeParameterAnnotationTest { + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface A { + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface B { + String value(); + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface C { + Class t(); + } + + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + private static @interface D { + int n(); + } + + static class U { + } + + <@A @B("foo") @C(t=U.class) @D(n=50) T> void setValue(T value) { + } + + @Test + void typeParameterAnnotation() { + try (final ScanResult scanResult = new ClassGraph().acceptPackages(TypeParameterAnnotationTest.class.getPackage().getName()) + .enableAllInfo().scan()) { + final ClassInfo cls = scanResult.getClassInfo(TypeParameterAnnotationTest.class.getName()); + final MethodInfo method = cls.getMethodInfo().get("setValue").get(0); + final TypeParameter typeParameter = method.getTypeSignatureOrTypeDescriptor().getTypeParameters().get(0); + final AnnotationInfoList annotationInfoList = typeParameter.getTypeAnnotationInfo(); + assertThat(annotationInfoList.get(0).toStringWithSimpleNames()).isEqualTo("@A"); + assertThat(annotationInfoList.get(1).toStringWithSimpleNames()).isEqualTo("@B(\"foo\")"); + assertThat(annotationInfoList.get(2).toStringWithSimpleNames()).isEqualTo("@C(t=U.class)"); + assertThat(annotationInfoList.get(3).toStringWithSimpleNames()).isEqualTo("@D(n=50)"); + } + } +} From f16cba0e7dc158bc34c77357e581dc62cdb076f3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 02:16:02 -0700 Subject: [PATCH 479/713] Fix ClassCastException (#705) --- .../classgraph/classpath/ClasspathOrder.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index d08755390..91106357c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -290,13 +290,16 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla } String pathElementStr; try { - // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped - pathElementStr = pathElement instanceof Path ? ((Path) pathElement).toUri().toString() - : pathElement.toString(); - // Windows paths ("C:\x\y") are encoded as "file:///C:/x/y" by Path.toUri().toString(), - // but then Paths.get() can't handle paths of the form "///C:/x/y" - if (pathElementStr.startsWith("file:///")) { - pathElementStr = ((Path) pathElement).toFile().toString(); + if (pathElement instanceof Path) { + // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped + pathElementStr = ((Path) pathElement).toUri().toString(); + // Windows paths ("C:\x\y") are encoded as "file:///C:/x/y" by Path.toUri().toString(), + // but then Paths.get() can't handle paths of the form "///C:/x/y" + if (pathElementStr.startsWith("file:///")) { + pathElementStr = ((Path) pathElement).toFile().toString(); + } + } else { + pathElementStr = pathElement.toString(); } } catch (IOError | SecurityException e) { pathElementStr = pathElement.toString(); From e9bbb178e8ac1e469be4db2c2c57d1401f6230ec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 02:45:05 -0700 Subject: [PATCH 480/713] Code cleanup --- .../io/github/classgraph/classpath/ClasspathOrder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 91106357c..e0043645b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -289,8 +289,8 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } String pathElementStr; - try { - if (pathElement instanceof Path) { + if (pathElement instanceof Path) { + try { // Path objects have to be converted to URIs before calling .toString(), otherwise scheme is dropped pathElementStr = ((Path) pathElement).toUri().toString(); // Windows paths ("C:\x\y") are encoded as "file:///C:/x/y" by Path.toUri().toString(), @@ -298,10 +298,10 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElementStr.startsWith("file:///")) { pathElementStr = ((Path) pathElement).toFile().toString(); } - } else { + } catch (IOError | SecurityException e) { pathElementStr = pathElement.toString(); } - } catch (IOError | SecurityException e) { + } else { pathElementStr = pathElement.toString(); } pathElementStr = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStr); From 93a8164d3957fa8eeb5a97c5080d38b4c124b755 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 03:43:42 -0700 Subject: [PATCH 481/713] Source > Cleanup --- .../java/io/github/classgraph/ClassInfo.java | 7 +++-- .../io/github/classgraph/TypeArgument.java | 6 ++-- .../io/github/classgraph/TypeParameter.java | 6 ++-- .../io/github/classgraph/utils/FileUtils.java | 4 +-- .../classgraph/issues/issue696/Issue696.java | 2 +- .../classgraph/issues/issue735/Issue735.java | 4 +-- .../issue741/TypeArgumentAnnotationTest.java | 31 ++++++++++++------- .../issue741/TypeParameterAnnotationTest.java | 29 ++++++++++------- 8 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index cb8e50d1e..d63e9a20d 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2933,10 +2933,11 @@ public ClassTypeSignature getTypeDescriptor() { } /** - * Returns the name of the source file this class has been compiled from, - * such as {@code ClassInfo.java} or {@code KClass.kt}. + * Returns the name of the source file this class has been compiled from, such as {@code ClassInfo.java} or + * {@code KClass.kt}. * - *

This field may be {@code null}. + *

+ * This field may be {@code null}. * * @return The name of the source file of this class, or {@code null} if not available */ diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 8ddacb1f5..b8afd0ff8 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -226,9 +226,11 @@ public void findReferencedClassNames(final Set refdClassNames) { } /** - * Get a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or null if none. + * Get a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or + * null if none. * - * @return a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or null if none. + * @return a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, + * or null if none. */ public AnnotationInfoList getTypeAnnotationInfo() { return typeAnnotationInfo; diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 420bd911a..f927a4621 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -98,9 +98,11 @@ public List getInterfaceBounds() { } /** - * Get a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if none. + * Get a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if + * none. * - * @return a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if none. + * @return a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if + * none. */ public AnnotationInfoList getTypeAnnotationInfo() { return typeAnnotationInfo; diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 07aa37e06..031815bcf 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -231,13 +231,13 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } else { pathSanitized.append(path); } - + // Intended to preserve the double slash at the start of UNC paths (#736). // e.g. //server/file/path if (pathHasInitialSlashSlash) { pathSanitized.insert(0, '/'); } - + int startIdx = 0; if (removeInitialSlash || !pathHasInitialSlash) { // Strip off leading "/" if it needs to be removed, or if it wasn't present in the original path diff --git a/src/test/java/io/github/classgraph/issues/issue696/Issue696.java b/src/test/java/io/github/classgraph/issues/issue696/Issue696.java index a295df078..fc9a6504b 100644 --- a/src/test/java/io/github/classgraph/issues/issue696/Issue696.java +++ b/src/test/java/io/github/classgraph/issues/issue696/Issue696.java @@ -28,7 +28,7 @@ public class Issue696 { public static class BrokenAnnotation { public class Dynamic { - public Dynamic(@Foo String param1, @Bar String param2) { + public Dynamic(@Foo final String param1, @Bar final String param2) { } } } diff --git a/src/test/java/io/github/classgraph/issues/issue735/Issue735.java b/src/test/java/io/github/classgraph/issues/issue735/Issue735.java index 23430538a..75d860c3a 100644 --- a/src/test/java/io/github/classgraph/issues/issue735/Issue735.java +++ b/src/test/java/io/github/classgraph/issues/issue735/Issue735.java @@ -26,10 +26,10 @@ static abstract class Derived2 implements Base { void genericSuperclass() { try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue735.class.getPackage().getName()) .enableAllInfo().ignoreClassVisibility().ignoreMethodVisibility().scan()) { - ClassInfo ci1 = scanResult.getClassInfo(Derived1.class.getName()); + final ClassInfo ci1 = scanResult.getClassInfo(Derived1.class.getName()); assertThat(ci1.getMethodInfo().get(0).getTypeSignatureOrTypeDescriptor().getResultType().toString()) .isEqualTo(String.class.getName()); - ClassInfo ci2 = scanResult.getClassInfo(Derived2.class.getName()); + final ClassInfo ci2 = scanResult.getClassInfo(Derived2.class.getName()); assertThat(ci2.getMethodInfo().get(0).getTypeSignatureOrTypeDescriptor().getResultType().toString()) .isEqualTo("T"); } diff --git a/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java index 90f8e6c97..e9364df0c 100644 --- a/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue741/TypeArgumentAnnotationTest.java @@ -1,30 +1,38 @@ package io.github.classgraph.issues.issue741; -import io.github.classgraph.*; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.AnnotationInfoList; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassRefTypeSignature; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.MethodParameterInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeArgument; public class TypeArgumentAnnotationTest { - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface A { } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface B { String value(); } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface C { Class t(); } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface D { int n(); } @@ -32,17 +40,18 @@ public class TypeArgumentAnnotationTest { static class U { } - void setValueList(List<@A @B("foo") @C(t=U.class) @D(n=50) ?> valueList) { + void setValueList(final List<@A @B("foo") @C(t = U.class) @D(n = 50) ?> valueList) { } @Test void typeArgumentAnnotation() { - try (final ScanResult scanResult = new ClassGraph().acceptPackages(TypeArgumentAnnotationTest.class.getPackage().getName()) - .enableAllInfo().scan()) { + try (final ScanResult scanResult = new ClassGraph() + .acceptPackages(TypeArgumentAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo cls = scanResult.getClassInfo(TypeArgumentAnnotationTest.class.getName()); final MethodInfo method = cls.getMethodInfo().get("setValueList").get(0); final MethodParameterInfo parameterInfo = method.getParameterInfo()[0]; - final TypeArgument typeArgument = ((ClassRefTypeSignature) parameterInfo.getTypeSignatureOrTypeDescriptor()).getTypeArguments().get(0); + final TypeArgument typeArgument = ((ClassRefTypeSignature) parameterInfo + .getTypeSignatureOrTypeDescriptor()).getTypeArguments().get(0); final AnnotationInfoList annotationInfoList = typeArgument.getTypeAnnotationInfo(); assertThat(annotationInfoList.get(0).toStringWithSimpleNames()).isEqualTo("@A"); assertThat(annotationInfoList.get(1).toStringWithSimpleNames()).isEqualTo("@B(\"foo\")"); diff --git a/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java index a67df1886..d00915824 100644 --- a/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue741/TypeParameterAnnotationTest.java @@ -1,29 +1,35 @@ package io.github.classgraph.issues.issue741; -import io.github.classgraph.*; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.lang.annotation.ElementType; import java.lang.annotation.Target; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.AnnotationInfoList; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeParameter; public class TypeParameterAnnotationTest { - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface A { } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface B { String value(); } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface C { Class t(); } - @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface D { int n(); } @@ -31,16 +37,17 @@ public class TypeParameterAnnotationTest { static class U { } - <@A @B("foo") @C(t=U.class) @D(n=50) T> void setValue(T value) { + <@A @B("foo") @C(t = U.class) @D(n = 50) T> void setValue(final T value) { } @Test void typeParameterAnnotation() { - try (final ScanResult scanResult = new ClassGraph().acceptPackages(TypeParameterAnnotationTest.class.getPackage().getName()) - .enableAllInfo().scan()) { + try (final ScanResult scanResult = new ClassGraph() + .acceptPackages(TypeParameterAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo cls = scanResult.getClassInfo(TypeParameterAnnotationTest.class.getName()); final MethodInfo method = cls.getMethodInfo().get("setValue").get(0); - final TypeParameter typeParameter = method.getTypeSignatureOrTypeDescriptor().getTypeParameters().get(0); + final TypeParameter typeParameter = method.getTypeSignatureOrTypeDescriptor().getTypeParameters() + .get(0); final AnnotationInfoList annotationInfoList = typeParameter.getTypeAnnotationInfo(); assertThat(annotationInfoList.get(0).toStringWithSimpleNames()).isEqualTo("@A"); assertThat(annotationInfoList.get(1).toStringWithSimpleNames()).isEqualTo("@B(\"foo\")"); From c0066556c81f073443de64313229599ec08839e4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 03:53:22 -0700 Subject: [PATCH 482/713] Fix unit test failure --- .../io/github/classgraph/utils/FastPathResolver.java | 8 +++----- .../nonapi/io/github/classgraph/utils/FileUtils.java | 3 ++- .../nonapi/io/github/classgraph/utils/VersionFinder.java | 9 ++++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 53ec286d6..9f3193b95 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -28,11 +28,12 @@ */ package nonapi.io.github.classgraph.utils; -import java.io.File; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; +import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; + /** * Resolve relative paths and URLs/URIs against a base path in a way that is faster than Java's URL/URI parser (and * much faster than Path), while aiming for cross-platform compatibility, and hopefully in particular being robust @@ -48,9 +49,6 @@ public final class FastPathResolver { /** Match custom URLs that are followed by one slash. */ private static final Pattern schemeOneSlashMatcher = Pattern.compile("^[a-zA-Z+\\-.]+:/"); - /** True if we're running on Windows. */ - private static final boolean WINDOWS = File.separatorChar == '\\'; - /** * Constructor. */ @@ -261,7 +259,7 @@ public static String resolve(final String resolveBasePath, final String relative } while (matchedPrefix); // Handle Windows paths starting with a drive designation as an absolute path - if (WINDOWS) { + if (VersionFinder.OS == OperatingSystem.Windows) { if (relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx)) { // Windows UNC path startIdx += 2; diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 031815bcf..b2df9866b 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -44,6 +44,7 @@ import java.util.concurrent.Callable; import nonapi.io.github.classgraph.reflection.ReflectionUtils; +import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; /** * File utilities. @@ -234,7 +235,7 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn // Intended to preserve the double slash at the start of UNC paths (#736). // e.g. //server/file/path - if (pathHasInitialSlashSlash) { + if (VersionFinder.OS == OperatingSystem.Windows && pathHasInitialSlashSlash) { pathSanitized.insert(0, '/'); } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java index 0fc43914d..8ee55625c 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.utils; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -137,12 +138,14 @@ public enum OperatingSystem { static { final String osName = getProperty("os.name", "unknown").toLowerCase(Locale.ENGLISH); - if (osName == null) { + if (File.separatorChar == '\\') { + OS = OperatingSystem.Windows; + } else if (osName == null) { OS = OperatingSystem.Unknown; - } else if (osName.contains("mac") || osName.contains("darwin")) { - OS = OperatingSystem.MacOSX; } else if (osName.contains("win")) { OS = OperatingSystem.Windows; + } else if (osName.contains("mac") || osName.contains("darwin")) { + OS = OperatingSystem.MacOSX; } else if (osName.contains("nux")) { OS = OperatingSystem.Linux; } else if (osName.contains("sunos") || osName.contains("solaris")) { From 6317d9e61688d573bae7d81a9b04a3bd25696ec2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 03:54:17 -0700 Subject: [PATCH 483/713] [maven-release-plugin] prepare release classgraph-4.8.153 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 82d7d4f88..e4ba1107b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.153-SNAPSHOT + 4.8.153 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.153 From 114277ed39e8a86215e8d9c4c386abd54e8c1b92 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 03:54:20 -0700 Subject: [PATCH 484/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e4ba1107b..c4d7d972c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.153 + 4.8.154-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.153 + classgraph-4.8.150 From 42753efb26cfa16be0daf761ca5a8e8c36401e40 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 21 Dec 2022 04:04:21 -0700 Subject: [PATCH 485/713] Refactoring so that all type signatures support getTypeAnnotationInfo() --- .../github/classgraph/HierarchicalTypeSignature.java | 9 +++++++++ src/main/java/io/github/classgraph/TypeArgument.java | 11 ----------- src/main/java/io/github/classgraph/TypeParameter.java | 11 ----------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index f0e9aea10..fe54eb0ae 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -61,6 +61,15 @@ void setScanResult(final ScanResult scanResult) { } } + /** + * Get a list of {@link AnnotationInfo} objects for any type annotations on this type, or null if none. + * + * @return a list of {@link AnnotationInfo} objects for any type annotations on this type, or null if none. + */ + public AnnotationInfoList getTypeAnnotationInfo() { + return typeAnnotationInfo; + } + /** * Add a type annotation. * diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index b8afd0ff8..302d57034 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -225,17 +225,6 @@ public void findReferencedClassNames(final Set refdClassNames) { } } - /** - * Get a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, or - * null if none. - * - * @return a list of {@link AnnotationInfo} objects for any type annotations before the type argument wildcard, - * or null if none. - */ - public AnnotationInfoList getTypeAnnotationInfo() { - return typeAnnotationInfo; - } - // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index f927a4621..e05936234 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -97,17 +97,6 @@ public List getInterfaceBounds() { return interfaceBounds; } - /** - * Get a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if - * none. - * - * @return a list of {@link AnnotationInfo} objects for any type annotations on this type parameter, or null if - * none. - */ - public AnnotationInfoList getTypeAnnotationInfo() { - return typeAnnotationInfo; - } - @Override protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { if (typePath.isEmpty()) { From 220451f7efc9310859f7f0264edd72914976ad4e Mon Sep 17 00:00:00 2001 From: Gili Tzabari Date: Thu, 22 Dec 2022 15:35:29 -0500 Subject: [PATCH 486/713] Fixed typo --- src/main/java/io/github/classgraph/ClassInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index d63e9a20d..867419e79 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1535,11 +1535,11 @@ public boolean hasFieldAnnotation(final String fieldAnnotationName) { } /** - * Checks whether this class declares a field of the given name. + * Checks whether this class declares a method of the given name. * * @param methodName * The name of a method. - * @return true if this class declares a field of the given name. + * @return true if this class declares a method of the given name. */ public boolean hasDeclaredMethod(final String methodName) { return getDeclaredMethodInfo().containsName(methodName); From c21fa516f4dbee1560179cac55f982f928bfd42f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 2 Jan 2023 13:57:03 -0700 Subject: [PATCH 487/713] Fix globs on classpath (#739) --- .../java/io/github/classgraph/Scanner.java | 2 +- .../classgraph/classpath/ClasspathOrder.java | 288 +++++++++--------- .../classgraph/issues/issue739/Issue739.java | 36 +++ 3 files changed, 182 insertions(+), 144 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue739/Issue739.java diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 0457e9ddb..6739f32c5 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -480,7 +480,7 @@ private static Object normalizeClasspathEntry(final Object classpathEntryObj) th // Canonicalize path, to avoid duplication // Throws IOException if the file does not exist or an I/O error occurs classpathEntryObjNormalized = ((Path) classpathEntryObjNormalized).toRealPath(); - } catch (final SecurityException e) { + } catch (final IOException | SecurityException e) { // Ignore } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index e0043645b..8152a7c4e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -309,6 +309,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } URL pathElementURL; + boolean hasWildcardSuffix = false; try { pathElementURL = pathElement instanceof URL ? (URL) pathElement : pathElement instanceof URI ? ((URI) pathElement).toURL() @@ -317,31 +318,42 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElementURL == null) { // Fallback -- call toString() on the path element, then try converting to a URL final String pathElementToStr = pathElement.toString(); - final boolean hasJarScheme = pathElementToStr.startsWith("jar:"); - int startIdx = hasJarScheme ? 4 : 0; - final Matcher m1 = schemeMatcher.matcher(pathElementToStr.substring(startIdx)); - String scheme = ""; - if (m1.find()) { - scheme = m1.group(); - startIdx += scheme.length(); - } - final String urlStr = (pathElementToStr.contains("!/") || hasJarScheme ? "jar:" : "") - + (scheme.isEmpty() ? "file:" : scheme) - // Escape '%' characters (#255) - + pathElementToStr.substring(startIdx).replace("%", "%25"); - try { - pathElementURL = new URL(urlStr); - } catch (final MalformedURLException e) { + if (pathElementToStr.endsWith("/*") || pathElementToStr.endsWith("\\*")) { + hasWildcardSuffix = true; + pathElementStr = pathElementToStr.substring(0, pathElementToStr.length() - 2); + // Leave pathElementURL null, so that wildcards can be handled below + } else if (pathElementToStr.equals("*")) { + hasWildcardSuffix = true; + pathElementStr = ""; + // Leave pathElementURL null, so that wildcards can be handled below + } else { + final boolean hasJarScheme = pathElementToStr.startsWith("jar:"); + int startIdx = hasJarScheme ? 4 : 0; + final Matcher m1 = schemeMatcher.matcher(pathElementToStr.substring(startIdx)); + String scheme = ""; + if (m1.find()) { + scheme = m1.group(); + startIdx += scheme.length(); + } + final String urlStr = (pathElementToStr.contains("!/") || hasJarScheme ? "jar:" : "") + + (scheme.isEmpty() ? "file:" : scheme) + // Escape '%' characters (#255) + + pathElementToStr.substring(startIdx).replace("%", "%25"); try { - pathElementURL = new File(pathElementToStr).toURI().toURL(); - } catch (final MalformedURLException | IllegalArgumentException | IOError - | SecurityException e1) { - if (log != null) { - log.log("Failed to convert classpath element to URL, " - + "Try prepending \"file:\" to create a URL (" + e1 + "): " + pathElementStr); + pathElementURL = new URL(urlStr); + } catch (final MalformedURLException e) { + try { + pathElementURL = new File(pathElementToStr).toURI().toURL(); + } catch (final MalformedURLException | IllegalArgumentException | IOError + | SecurityException e1) { + if (log != null) { + log.log("Failed to convert classpath element to URL, " + + "Try prepending \"file:\" to create a URL (" + e1 + "): " + + pathElementStr); + } + // Final fallback -- try just using the raw string as a URL + pathElementURL = new URL(pathElementToStr); } - // Final fallback -- try just using the raw string as a URL - pathElementURL = new URL(pathElementToStr); } } } @@ -375,138 +387,128 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla } return false; } - } else { - // Check for wildcard path element (allowable for local classpaths as of JDK 6) - if (pathElementStr.endsWith("*")) { - if (pathElementStr.length() == 1 || // - (pathElementStr.length() > 2 && pathElementStr.charAt(pathElementStr.length() - 1) == '*' - && (pathElementStr.charAt(pathElementStr.length() - 2) == File.separatorChar - || (File.separatorChar != '/' - && pathElementStr.charAt(pathElementStr.length() - 2) == '/')))) { - // Apply classpath element filters, if any - final String baseDirPath = pathElementStr.length() == 1 ? "" - : pathElementStr.substring(0, pathElementStr.length() - 2); - final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), - baseDirPath); - if (!filter(pathElementURL, baseDirPath) || (!baseDirPathResolved.equals(baseDirPath) - && !filter(pathElementURL, baseDirPathResolved))) { - if (log != null) { - log.log("Classpath element did not match filter criterion, skipping: " - + pathElementStr); - } - return false; - } + } + if (hasWildcardSuffix) { + // Has wildcard path element (allowable for local classpaths as of JDK 6) + // Apply classpath element filters, if any + final String baseDirPath = pathElementStr; + final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), baseDirPath); + if (!filter(pathElementURL, baseDirPath) + || (!baseDirPathResolved.equals(baseDirPath) && !filter(pathElementURL, baseDirPathResolved))) { + if (log != null) { + log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); + } + return false; + } - // Check the path before the "/*" suffix is a directory - final File baseDir = new File(baseDirPathResolved); - if (!baseDir.exists()) { - if (log != null) { - log.log("Directory does not exist for wildcard classpath element: " + pathElementStr); - } - return false; - } - if (!FileUtils.canRead(baseDir)) { - if (log != null) { - log.log("Cannot read directory for wildcard classpath element: " + pathElementStr); - } - return false; - } - if (!baseDir.isDirectory()) { - if (log != null) { - log.log("Wildcard is appended to something other than a directory: " + pathElementStr); - } - return false; - } + // Check the path before the "/*" suffix is a directory + final File baseDir = new File(baseDirPathResolved); + if (!baseDir.exists()) { + if (log != null) { + log.log("Directory does not exist for wildcard classpath element: " + pathElementStr); + } + return false; + } + if (!FileUtils.canRead(baseDir)) { + if (log != null) { + log.log("Cannot read directory for wildcard classpath element: " + pathElementStr); + } + return false; + } + if (!baseDir.isDirectory()) { + if (log != null) { + log.log("Wildcard is appended to something other than a directory: " + pathElementStr); + } + return false; + } - // Add all elements in the requested directory to the classpath - final LogNode dirLog = log == null ? null - : log.log("Adding classpath elements from wildcarded directory: " + pathElementStr); - final File[] baseDirFiles = baseDir.listFiles(); - if (baseDirFiles != null) { - for (final File fileInDir : baseDirFiles) { - final String name = fileInDir.getName(); - if (!name.equals(".") && !name.equals("..")) { - // Add each directory entry as a classpath element - final String fileInDirPath = fileInDir.getPath(); - final String fileInDirPathResolved = FastPathResolver - .resolve(FileUtils.currDirPath(), fileInDirPath); - if (addClasspathEntry(fileInDirPathResolved, fileInDirPathResolved, classLoader, - scanSpec)) { - if (dirLog != null) { - dirLog.log("Found classpath element: " + fileInDirPath - + (fileInDirPath.equals(fileInDirPathResolved) ? "" - : " -> " + fileInDirPathResolved)); - } - } else { - if (dirLog != null) { - dirLog.log("Ignoring duplicate classpath element: " + fileInDirPath - + (fileInDirPath.equals(fileInDirPathResolved) ? "" - : " -> " + fileInDirPathResolved)); - } - } + // Add all elements in the requested directory to the classpath + final LogNode dirLog = log == null ? null + : log.log("Adding classpath elements from wildcarded directory: " + pathElementStr); + final File[] baseDirFiles = baseDir.listFiles(); + if (baseDirFiles != null) { + for (final File fileInDir : baseDirFiles) { + final String name = fileInDir.getName(); + if (!name.equals(".") && !name.equals("..")) { + // Add each directory entry as a classpath element + final String fileInDirPath = fileInDir.getPath(); + final String fileInDirPathResolved = FastPathResolver.resolve(FileUtils.currDirPath(), + fileInDirPath); + if (addClasspathEntry(fileInDirPathResolved, fileInDirPathResolved, classLoader, + scanSpec)) { + if (dirLog != null) { + dirLog.log("Found classpath element: " + fileInDirPath + + (fileInDirPath.equals(fileInDirPathResolved) ? "" + : " -> " + fileInDirPathResolved)); + } + } else { + if (dirLog != null) { + dirLog.log("Ignoring duplicate classpath element: " + fileInDirPath + + (fileInDirPath.equals(fileInDirPathResolved) ? "" + : " -> " + fileInDirPathResolved)); } } - return true; - } else { - return false; } - } else { - if (log != null) { - log.log("Wildcard classpath elements can only end with a leaf of \"*\", " - + "can't have a partial name and then a wildcard: " + pathElementStr); - } - return false; } + return true; } else { - // Non-wildcarded (standard) classpath element - final String pathElementResolved = FastPathResolver.resolve(FileUtils.currDirPath(), - pathElementStr); - if (!filter(pathElementURL, pathElementStr) || (!pathElementResolved.equals(pathElementStr) - && !filter(pathElementURL, pathElementResolved))) { - if (log != null) { - log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr - + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return false; + return false; + } + } else { + // Non-wildcarded (standard) classpath element + if (pathElementStr.indexOf('*') >= 0) { + if (log != null) { + log.log("Wildcard classpath elements can only end with a suffix of \"/*\", " + + "can't use globs elsewhere in the path: " + pathElementStr); } - if (pathElementResolved.startsWith("//")) { - // Handle Windows UNC paths (#705). - // File supports UNC paths directly: - // https://wiki.eclipse.org/Eclipse/UNC_Paths#Programming_with_UNC_paths - try { - final File file = new File(pathElementResolved); - if (addClasspathEntry(file, pathElementResolved, classLoader, scanSpec)) { - if (log != null) { - log.log("Found classpath element: " + file - + (pathElementStr.equals(pathElementResolved) ? "" - : " -> " + pathElementResolved)); - } - return true; - } else { - if (log != null) { - log.log("Ignoring duplicate classpath element: " + pathElementStr - + (pathElementStr.equals(pathElementResolved) ? "" - : " -> " + pathElementResolved)); - } - return false; + return false; + } + final String pathElementResolved = FastPathResolver.resolve(FileUtils.currDirPath(), pathElementStr); + if (!filter(pathElementURL, pathElementStr) || (!pathElementResolved.equals(pathElementStr) + && !filter(pathElementURL, pathElementResolved))) { + if (log != null) { + log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); + } + return false; + } + if (pathElementResolved.startsWith("//")) { + // Handle Windows UNC paths (#705). + // File supports UNC paths directly: + // https://wiki.eclipse.org/Eclipse/UNC_Paths#Programming_with_UNC_paths + try { + final File file = new File(pathElementResolved); + if (addClasspathEntry(file, pathElementResolved, classLoader, scanSpec)) { + if (log != null) { + log.log("Found classpath element: " + file + + (pathElementStr.equals(pathElementResolved) ? "" + : " -> " + pathElementResolved)); } - } catch (final Exception e) { - // Fall through + return true; + } else { + if (log != null) { + log.log("Ignoring duplicate classpath element: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" + : " -> " + pathElementResolved)); + } + return false; } + } catch (final Exception e) { + // Fall through } - if (addClasspathEntry(pathElementResolved, pathElementResolved, classLoader, scanSpec)) { - if (log != null) { - log.log("Found classpath element: " + pathElementStr - + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return true; - } else { - if (log != null) { - log.log("Ignoring duplicate classpath element: " + pathElementStr - + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return false; + } + if (addClasspathEntry(pathElementResolved, pathElementResolved, classLoader, scanSpec)) { + if (log != null) { + log.log("Found classpath element: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); } + return true; + } else { + if (log != null) { + log.log("Ignoring duplicate classpath element: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); + } + return false; } } } diff --git a/src/test/java/io/github/classgraph/issues/issue739/Issue739.java b/src/test/java/io/github/classgraph/issues/issue739/Issue739.java new file mode 100644 index 000000000..fa70d3402 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue739/Issue739.java @@ -0,0 +1,36 @@ +package io.github.classgraph.issues.issue739; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashSet; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.Resource; +import io.github.classgraph.ScanResult; + +public class Issue739 { + @Test + void wildcardPathSupport() { + final String relPath = "src/test/resources/"; + final HashSet paths = new HashSet<>(); + try (ScanResult scanResult = new ClassGraph().overrideClasspath(relPath + "*").scan()) { + scanResult.getAllResources().forEach(new Consumer() { + @Override + public void accept(final Resource r) { + final String path = r.toString(); + final int idx = path.indexOf(relPath); + if (idx >= 0) { + paths.add(path.substring(idx + relPath.length())); + } + } + }); + } + assertThat(paths).contains("issue673/a.zip"); + assertThat(paths).contains("multi-release-jar.src.zip!/multi-release-jar/src/main/java-9/module-info.java"); + assertThat(paths).contains("zip64.zip!/10046"); + assertThat(paths).contains("record.jar!/pkg/Record.class"); + } +} From 92b644677964496fb841ba41bed52247f8a24786 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 2 Jan 2023 13:58:20 -0700 Subject: [PATCH 488/713] [maven-release-plugin] prepare release classgraph-4.8.154 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c4d7d972c..77b2a2478 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.154-SNAPSHOT + 4.8.154 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.154 From 8b358c63db2b4a5dc844f96e9453809b8f83adf8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 2 Jan 2023 13:58:23 -0700 Subject: [PATCH 489/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 77b2a2478..e61aef813 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.154 + 4.8.155-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.154 + classgraph-4.8.150 From 708ae1b9488ebe4de34f947c43c507e18424af95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 02:01:09 +0000 Subject: [PATCH 490/713] Bump junit-jupiter from 5.9.1 to 5.9.2 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.9.1 to 5.9.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.1...r5.9.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61aef813..073848379 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ org.junit.jupiter junit-jupiter - 5.9.1 + 5.9.2 test From 97913986e08997c1205c8e0778924a8c0e443bbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 02:00:28 +0000 Subject: [PATCH 491/713] Bump assertj-core from 3.23.1 to 3.24.2 Bumps assertj-core from 3.23.1 to 3.24.2. --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61aef813..fb84b753f 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ org.assertj assertj-core - 3.23.1 + 3.24.2 test From 1c8b2829fc95f4bbb2ba38d09d5fcbd1b114c511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 02:01:16 +0000 Subject: [PATCH 492/713] Bump maven-enforcer-plugin from 3.1.0 to 3.2.1 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.1.0 to 3.2.1. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.1.0...enforcer-3.2.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61aef813..ef0ad12d2 100644 --- a/pom.xml +++ b/pom.xml @@ -164,7 +164,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.1.0 + 3.2.1 org.apache.maven.plugins From e8d8861e9ff95f33271ec42fbb7bb8d712fd5de4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 02:56:26 +0000 Subject: [PATCH 493/713] Bump maven-deploy-plugin from 3.0.0 to 3.1.0 Bumps [maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.0.0...maven-deploy-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61aef813..04b616afc 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins From 3bda3be101f602a83ae9356d7168c9fa218f4cb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 02:56:30 +0000 Subject: [PATCH 494/713] Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M9 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M7 to 3.0.0-M9. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M7...surefire-3.0.0-M9) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61aef813..0b2e487aa 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.0.0-M9 org.codehaus.mojo From 955baf59b9788d50f1854f120bd937eff0b9409e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 27 Feb 2023 15:53:36 -0700 Subject: [PATCH 495/713] Release static references after scan is complete (#756) --- .../java/io/github/classgraph/ScanResult.java | 3 + .../classgraph/classpath/CallStackReader.java | 138 +++++++++--------- .../classgraph/classpath/ClasspathFinder.java | 4 + .../reflection/ReflectionUtils.java | 72 ++++++--- .../classpath/ClasspathFinderTest.java | 5 +- 5 files changed, 134 insertions(+), 88 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 00cc9334f..14d87fd00 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -56,6 +56,7 @@ import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; +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.Assert; @@ -1594,6 +1595,8 @@ public void close() { if (topLevelLog != null) { topLevelLog.flush(); } + // Unload the reflection driver (#756) + ReflectionUtils.unloadReflectionDriver(); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index 7bd7c0cc5..2c7ec2489 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -42,8 +42,6 @@ /** A class to find the unique ordered classpath elements. */ class CallStackReader { - private static Class[] callStack; - /** * Constructor. */ @@ -141,82 +139,82 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { * @return The classes in the call stack. */ static Class[] getClassContext(final LogNode log) { - if (callStack == null) { - // For JRE 9+, use StackWalker to get call stack. - if (VersionFinder.JAVA_MAJOR_VERSION == 9 // - || VersionFinder.JAVA_MAJOR_VERSION == 10 // - || (VersionFinder.JAVA_MAJOR_VERSION == 11 // - && VersionFinder.JAVA_MINOR_VERSION == 0 - && (VersionFinder.JAVA_SUB_VERSION < 4 - || (VersionFinder.JAVA_SUB_VERSION == 4 && VersionFinder.JAVA_IS_EA_VERSION))) - || (VersionFinder.JAVA_MAJOR_VERSION == 12 && VersionFinder.JAVA_MINOR_VERSION == 0 - && (VersionFinder.JAVA_SUB_VERSION < 2 || (VersionFinder.JAVA_SUB_VERSION == 2 - && VersionFinder.JAVA_IS_EA_VERSION)))) { - // Don't trigger the StackWalker bug that crashed the JVM, which was fixed in JDK 13, - // and backported to 12.0.2 and 11.0.4 (probably introduced in JDK 9, when StackWalker - // was introduced): - // https://github.com/classgraph/classgraph/issues/341 - // https://bugs.openjdk.java.net/browse/JDK-8210457 - // -- fall through - } else { - // Get the stack via StackWalker. - // Invoke with doPrivileged -- see: - // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html - try { - callStack = ReflectionUtils.doPrivileged(new Callable[]>() { - @Override - public Class[] call() throws Exception { - return getCallStackViaStackWalker(); - } - }); - } catch (final Throwable e) { - // Fall through - } + Class[] callStack = null; + + // For JRE 9+, use StackWalker to get call stack. + if (VersionFinder.JAVA_MAJOR_VERSION == 9 // + || VersionFinder.JAVA_MAJOR_VERSION == 10 // + || (VersionFinder.JAVA_MAJOR_VERSION == 11 // + && VersionFinder.JAVA_MINOR_VERSION == 0 + && (VersionFinder.JAVA_SUB_VERSION < 4 + || (VersionFinder.JAVA_SUB_VERSION == 4 && VersionFinder.JAVA_IS_EA_VERSION))) + || (VersionFinder.JAVA_MAJOR_VERSION == 12 && VersionFinder.JAVA_MINOR_VERSION == 0 + && (VersionFinder.JAVA_SUB_VERSION < 2 + || (VersionFinder.JAVA_SUB_VERSION == 2 && VersionFinder.JAVA_IS_EA_VERSION)))) { + // Don't trigger the StackWalker bug that crashed the JVM, which was fixed in JDK 13, + // and backported to 12.0.2 and 11.0.4 (probably introduced in JDK 9, when StackWalker + // was introduced): + // https://github.com/classgraph/classgraph/issues/341 + // https://bugs.openjdk.java.net/browse/JDK-8210457 + // -- fall through + } else { + // Get the stack via StackWalker. + // Invoke with doPrivileged -- see: + // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html + try { + callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + @Override + public Class[] call() throws Exception { + return getCallStackViaStackWalker(); + } + }); + } catch (final Throwable e) { + // Fall through } + } - // For JRE 7 and 8, use SecurityManager to get call stack (don't use this method on JDK 9+, - // because it will result in a reflective illegal access warning, see #663) - if (VersionFinder.JAVA_MAJOR_VERSION < 9 && (callStack == null || callStack.length == 0)) { - try { - callStack = ReflectionUtils.doPrivileged(new Callable[]>() { - @Override - public Class[] call() throws Exception { - return getCallStackViaSecurityManager(log); - } - }); - } catch (final Throwable e) { - // Fall through - } + // For JRE 7 and 8, use SecurityManager to get call stack (don't use this method on JDK 9+, + // because it will result in a reflective illegal access warning, see #663) + if (VersionFinder.JAVA_MAJOR_VERSION < 9 && (callStack == null || callStack.length == 0)) { + try { + callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + @Override + public Class[] call() throws Exception { + return getCallStackViaSecurityManager(log); + } + }); + } catch (final Throwable e) { + // Fall through } + } - // As a fallback, use getStackTrace() to try to get the call stack - if (callStack == null || callStack.length == 0) { - StackTraceElement[] stackTrace = null; + // As a fallback, use getStackTrace() to try to get the call stack + if (callStack == null || callStack.length == 0) { + StackTraceElement[] stackTrace = null; + try { + stackTrace = Thread.currentThread().getStackTrace(); + } catch (final SecurityException e) { + // Fall through + } + if (stackTrace == null || stackTrace.length == 0) { try { - stackTrace = Thread.currentThread().getStackTrace(); - } catch (final SecurityException e) { - // Fall through - } - if (stackTrace == null || stackTrace.length == 0) { - try { - // Try getting stacktrace by throwing an exception - throw new Exception(); - } catch (final Exception e) { - stackTrace = e.getStackTrace(); - } + // Try getting stacktrace by throwing an exception + throw new Exception(); + } catch (final Exception e) { + stackTrace = e.getStackTrace(); } - final List> stackClassesList = new ArrayList<>(); - for (final StackTraceElement elt : stackTrace) { - try { - stackClassesList.add(Class.forName(elt.getClassName())); - } catch (final ClassNotFoundException | LinkageError ignored) { - // Ignored - } - } - if (!stackClassesList.isEmpty()) { - callStack = stackClassesList.toArray(new Class[0]); + } + final List> stackClassesList = new ArrayList<>(); + for (final StackTraceElement elt : stackTrace) { + try { + stackClassesList.add(Class.forName(elt.getClassName())); + } catch (final ClassNotFoundException | LinkageError ignored) { + // Ignored } } + if (!stackClassesList.isEmpty()) { + callStack = stackClassesList.toArray(new Class[0]); + } } // Last-ditch effort -- include just this class in the call stack diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 45972e80e..c8791c826 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -36,6 +36,7 @@ import io.github.classgraph.ClassGraphClassLoader; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry.ClassLoaderHandlerRegistryEntry; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -117,6 +118,9 @@ public ClassGraphClassLoader getDelegateClassGraphClassLoader() { public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); + // Ensure reflection driver is loaded + ReflectionUtils.loadReflectionDriver(); + // Require scanning traditional classpath if an override classloader is AppClassLoader (#639) boolean forceScanJavaClassPath = false; diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index cad2cef78..360bab76d 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -41,43 +41,54 @@ public final class ReflectionUtils { /** The reflection driver to use. */ public static ReflectionDriver reflectionDriver; - private static Class accessControllerClass; private static Class privilegedActionClass; private static Method accessControllerDoPrivileged; - static { - loadReflectionDriver(); - - try { - accessControllerClass = Class.forName("java.security.AccessController"); - privilegedActionClass = Class.forName("java.security.PrivilegedAction"); - accessControllerDoPrivileged = accessControllerClass.getMethod("doPrivileged", privilegedActionClass); - } catch (final Throwable t) { - // Ignore - } - } - /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ public static void loadReflectionDriver() { - if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NARCISSUS) { + if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NARCISSUS + && (reflectionDriver == null || !(reflectionDriver instanceof NarcissusReflectionDriver))) { try { reflectionDriver = new NarcissusReflectionDriver(); } catch (final Throwable t) { System.err.println("Could not load Narcissus reflection driver: " + t); // Fall back to standard reflection driver } - } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER) { + } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER + && (reflectionDriver == null || !(reflectionDriver instanceof JVMDriverReflectionDriver))) { try { reflectionDriver = new JVMDriverReflectionDriver(); } catch (final Throwable t) { System.err.println("Could not load JVM-Driver reflection driver: " + t); // Fall back to standard reflection driver } - } - if (reflectionDriver == null) { + } else if (reflectionDriver == null + || ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NONE) { reflectionDriver = new StandardReflectionDriver(); } + try { + if (accessControllerClass == null) { + accessControllerClass = Class.forName("java.security.AccessController"); + } + if (privilegedActionClass == null) { + privilegedActionClass = Class.forName("java.security.PrivilegedAction"); + } + if (accessControllerDoPrivileged == null) { + accessControllerDoPrivileged = accessControllerClass.getMethod("doPrivileged", + privilegedActionClass); + } + } catch (final Throwable t) { + // Ignore + } + } + + /** Driver must be unloaded at end of scan to prevent reference being held (#756) */ + public static void unloadReflectionDriver() { + reflectionDriver = null; + accessControllerClass = null; + privilegedActionClass = null; + accessControllerDoPrivileged = null; } /** @@ -106,6 +117,9 @@ private ReflectionUtils() { */ public static Object getFieldVal(final boolean throwException, final Object obj, final Field field) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (obj == null || field == null) { if (throwException) { throw new NullPointerException(); @@ -143,6 +157,9 @@ public static Object getFieldVal(final boolean throwException, final Object obj, */ public static Object getFieldVal(final boolean throwException, final Object obj, final String fieldName) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (obj == null || fieldName == null) { if (throwException) { throw new NullPointerException(); @@ -180,6 +197,9 @@ public static Object getFieldVal(final boolean throwException, final Object obj, */ public static Object getStaticFieldVal(final boolean throwException, final Class cls, final String fieldName) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (cls == null || fieldName == null) { if (throwException) { throw new NullPointerException(); @@ -217,6 +237,9 @@ public static Object getStaticFieldVal(final boolean throwException, final Class */ public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (obj == null || methodName == null) { if (throwException) { throw new IllegalArgumentException("Unexpected null argument"); @@ -257,6 +280,9 @@ public static Object invokeMethod(final boolean throwException, final Object obj */ public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName, final Class argType, final Object param) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (obj == null || methodName == null || argType == null) { if (throwException) { throw new IllegalArgumentException("Unexpected null argument"); @@ -294,6 +320,9 @@ public static Object invokeMethod(final boolean throwException, final Object obj */ public static Object invokeStaticMethod(final boolean throwException, final Class cls, final String methodName) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (cls == null || methodName == null) { if (throwException) { throw new IllegalArgumentException("Unexpected null argument"); @@ -335,6 +364,9 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas */ public static Object invokeStaticMethod(final boolean throwException, final Class cls, final String methodName, final Class argType, final Object param) throws IllegalArgumentException { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } if (cls == null || methodName == null || argType == null) { if (throwException) { throw new IllegalArgumentException("Unexpected null argument"); @@ -362,6 +394,9 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas * @return The class of the requested name, or null if an exception was thrown while trying to load the class. */ public static Class classForNameOrNull(final String className) { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } try { return reflectionDriver.findClass(className); } catch (final Throwable e) { @@ -377,6 +412,9 @@ public static Class classForNameOrNull(final String className) { * @return The class of the requested name, or null if an exception was thrown while trying to load the class. */ public static Method staticMethodForNameOrNull(final String className, final String staticMethodName) { + if (reflectionDriver == null) { + throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); + } try { return reflectionDriver.findStaticMethod(reflectionDriver.findClass(className), staticMethodName); } catch (final Throwable e) { diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java index f5def903d..ce9582c47 100644 --- a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.io.TempDir; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -102,7 +103,8 @@ public void testOverrideClasspathAndEnableSystemModules(@TempDir final Path tmpD // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); - + ReflectionUtils.unloadReflectionDriver(); + // Assert assertNotNull(moduleFinder, "ModuleFinder should be non-null"); assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); @@ -134,6 +136,7 @@ public void testOverrideClassLoaderAndEnableSystemModules(@TempDir final Path tm // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); + ReflectionUtils.unloadReflectionDriver(); // Assert assertNotNull(moduleFinder, "ModuleFinder should be non-null"); From 68bd2ecc6877a4111b0747dd53f71b02297d8d6e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 27 Feb 2023 15:54:03 -0700 Subject: [PATCH 496/713] [maven-release-plugin] prepare release classgraph-4.8.155 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e61aef813..46a1c5e8c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.155-SNAPSHOT + 4.8.155 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.155 From 9a84cd9788a39bb19e00278bad18fd72838b372f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 27 Feb 2023 15:54:06 -0700 Subject: [PATCH 497/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 46a1c5e8c..d1108f7c0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.155 + 4.8.156-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.155 + classgraph-4.8.150 From 2bb319215c0d9179099b8d45df25f39832a39335 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Mar 2023 00:53:47 -0700 Subject: [PATCH 498/713] Make ReflectionUtils non-static (#756, #757) --- .../io/github/classgraph/AnnotationInfo.java | 5 +- .../java/io/github/classgraph/ClassGraph.java | 12 +- .../java/io/github/classgraph/ClassInfo.java | 4 +- .../io/github/classgraph/ModulePathInfo.java | 8 +- .../github/classgraph/ModuleReaderProxy.java | 34 ++-- .../java/io/github/classgraph/ModuleRef.java | 26 +-- .../java/io/github/classgraph/ScanResult.java | 12 +- .../java/io/github/classgraph/Scanner.java | 8 +- .../AntClassLoaderHandler.java | 5 +- .../CxfContainerClassLoaderHandler.java | 4 +- .../EquinoxClassLoaderHandler.java | 57 +++--- ...quinoxContextFinderClassLoaderHandler.java | 6 +- .../FallbackClassLoaderHandler.java | 187 ++++++++++-------- .../FelixClassLoaderHandler.java | 26 +-- .../JBossClassLoaderHandler.java | 40 ++-- .../JPMSClassLoaderHandler.java | 5 +- .../OSGiDefaultClassLoaderHandler.java | 13 +- ...DelegationOrderTestClassLoaderHandler.java | 5 +- ...assWorldsClassRealmClassLoaderHandler.java | 18 +- .../QuarkusClassLoaderHandler.java | 28 +-- .../TomcatWebappClassLoaderBaseHandler.java | 33 ++-- .../UnoOneJarClassLoaderHandler.java | 4 +- .../WeblogicClassLoaderHandler.java | 9 +- .../WebsphereLibertyClassLoaderHandler.java | 38 ++-- ...ebsphereTraditionalClassLoaderHandler.java | 4 +- .../classgraph/classpath/CallStackReader.java | 12 +- .../classpath/ClassLoaderFinder.java | 5 +- .../classpath/ClassLoaderOrder.java | 7 + .../classgraph/classpath/ClasspathFinder.java | 17 +- .../classgraph/classpath/ClasspathOrder.java | 6 +- .../classgraph/classpath/ModuleFinder.java | 26 +-- .../fastzipfilereader/NestedJarHandler.java | 29 ++- .../classgraph/fileslice/FileSlice.java | 17 +- .../classgraph/json/ClassFieldCache.java | 16 +- .../github/classgraph/json/ClassFields.java | 5 +- .../classgraph/json/JSONDeserializer.java | 51 ++++- .../classgraph/json/JSONSerializer.java | 56 +++++- .../io/github/classgraph/json/JSONUtils.java | 9 +- .../reflection/ReflectionDriver.java | 4 +- .../reflection/ReflectionUtils.java | 107 ++++------ .../reflection/StandardReflectionDriver.java | 47 ++++- .../io/github/classgraph/utils/FileUtils.java | 37 ++-- .../EncapsulationCircumventionTest.java | 12 +- .../json/JSONSerializationTest.java | 2 + .../classpath/ClasspathFinderTest.java | 10 +- 45 files changed, 641 insertions(+), 425 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 1228b06cc..cbf6425ee 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -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 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 diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index a11e6371b..c29683cfc 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -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; @@ -126,6 +127,8 @@ public enum CircumventEncapsulationMethod { */ public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; + private final ReflectionUtils reflectionUtils; + /** * If non-null, log while scanning. */ @@ -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); } /** @@ -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); @@ -1529,7 +1533,7 @@ private Future 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). @@ -1764,7 +1768,7 @@ public List getModules() { * @return The {@link ModulePathInfo}. */ public ModulePathInfo getModulePathInfo() { - scanSpec.modulePathInfo.getRuntimeInfo(); + scanSpec.modulePathInfo.getRuntimeInfo(reflectionUtils); return scanSpec.modulePathInfo; } } diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 867419e79..9099c697a 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2718,8 +2718,10 @@ public List getEnumConstantObjects() { final Class enumClass = loadClass(); final FieldInfoList consts = getEnumConstants(); final List 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"); } diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 998f62710..3992c2048 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -131,7 +131,7 @@ 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)) { @@ -139,14 +139,14 @@ void getRuntimeInfo() { // 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 commandlineArguments = runtimeMXBean == null ? null - : (List) ReflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean, + : (List) reflectionUtils.invokeMethod(/* throwException = */ false, runtimeMXBean, "getInputArguments"); if (commandlineArguments != null) { for (final String arg : commandlineArguments) { diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index bdda3c58f..7819fdbf7 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -47,14 +47,7 @@ public class ModuleReaderProxy implements Closeable { /** Collector> 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. @@ -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"); @@ -104,12 +106,12 @@ public List list() throws SecurityException { if (collectorsToList == null) { throw new IllegalArgumentException("Could not call Collectors.toList()"); } - final Object /* Stream */ resourcesStream = ReflectionUtils + final Object /* Stream */ 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())"); @@ -132,12 +134,12 @@ public List list() throws SecurityException { * If the module cannot be accessed. */ public InputStream open(final String path) throws SecurityException { - final Object /* Optional */ optionalInputStream = ReflectionUtils + final Object /* Optional */ 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()"); @@ -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 */ optionalByteBuffer = ReflectionUtils + final Object /* Optional */ 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()"); @@ -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); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 96a9b838b..a6496280a 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -70,6 +70,8 @@ public class ModuleRef implements Comparable { /** The ClassLoader that loads classes in the module. May be null, to represent the bootstrap classloader. */ private final ClassLoader classLoader; + ReflectionUtils reflectionUtils; + /** * Constructor. * @@ -78,7 +80,8 @@ public class ModuleRef implements Comparable { * @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"); } @@ -87,15 +90,16 @@ 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 modulePackages = (Set) ReflectionUtils.invokeMethod(/* throwException = */ true, + final Set modulePackages = (Set) reflectionUtils.invokeMethod(/* throwException = */ true, this.descriptor, "packages"); if (modulePackages == null) { // Should not happen @@ -103,30 +107,30 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { } 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 @@ -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); } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 14d87fd00..e69e19b38 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -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; @@ -206,7 +208,7 @@ 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 @@ -214,7 +216,7 @@ static void init() { // 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); } } @@ -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) { @@ -450,7 +453,7 @@ public List getModules() { * @return The {@link ModulePathInfo}. */ public ModulePathInfo getModulePathInfo() { - scanSpec.modulePathInfo.getRuntimeInfo(); + scanSpec.modulePathInfo.getRuntimeInfo(reflectionUtils); return scanSpec.modulePathInfo; } @@ -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(); } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 6739f32c5..857f863ec 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -70,6 +70,7 @@ import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.concurrency.WorkQueue.WorkUnitProcessor; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -138,7 +139,8 @@ class Scanner implements Callable { */ Scanner(final boolean performScan, final ScanSpec scanSpec, final ExecutorService executorService, final int numParallelTasks, final ScanResultProcessor scanResultProcessor, - final FailureHandler failureHandler, final LogNode topLevelLog) throws InterruptedException { + final FailureHandler failureHandler, final ReflectionUtils reflectionUtils, final LogNode topLevelLog) + throws InterruptedException { this.scanSpec = scanSpec; this.performScan = performScan; scanSpec.sortPrefixes(); @@ -156,14 +158,14 @@ class Scanner implements Callable { this.interruptionChecker = executorService instanceof AutoCloseableExecutorService ? ((AutoCloseableExecutorService) executorService).interruptionChecker : new InterruptionChecker(); - this.nestedJarHandler = new NestedJarHandler(scanSpec, interruptionChecker); + this.nestedJarHandler = new NestedJarHandler(scanSpec, interruptionChecker, reflectionUtils); this.numParallelTasks = numParallelTasks; this.scanResultProcessor = scanResultProcessor; this.failureHandler = failureHandler; this.topLevelLog = topLevelLog; final LogNode classpathFinderLog = topLevelLog == null ? null : topLevelLog.log("Finding classpath"); - this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); + this.classpathFinder = new ClasspathFinder(scanSpec, reflectionUtils, classpathFinderLog); try { this.moduleOrder = new ArrayList<>(); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java index f20673fd9..6489ecbec 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -84,7 +83,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { classpathOrder.addClasspathPathStr( - (String) ReflectionUtils.invokeMethod(false, classLoader, "getClasspath"), classLoader, scanSpec, - log); + (String) classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getClasspath"), + classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java index b5664f247..62af46256 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -74,7 +73,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // Ignore } // tccl = TomcatClassLoader - classLoaderOrder.delegateTo((ClassLoader) ReflectionUtils.invokeMethod(false, classLoader, "tccl"), + classLoaderOrder.delegateTo( + (ClassLoader) classLoaderOrder.reflectionUtils.invokeMethod(false, classLoader, "tccl"), /* isParent = */ false, log); // This classloader doesn't actually load any classes, but add it to the order to improve logging classLoaderOrder.add(classLoader, log); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java index aaf8b315d..9e387f29e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -34,7 +34,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -106,11 +105,12 @@ private static void addBundleFile(final Object bundlefile, final Set pat // Don't get stuck in infinite loop if (bundlefile != null && path.add(bundlefile)) { // type File - final Object baseFile = ReflectionUtils.getFieldVal(false, bundlefile, "basefile"); + final Object baseFile = classpathOrderOut.reflectionUtils.getFieldVal(false, bundlefile, "basefile"); if (baseFile != null) { boolean foundClassPathElement = false; for (final String fieldName : FIELD_NAMES) { - final Object fieldVal = ReflectionUtils.getFieldVal(false, bundlefile, fieldName); + final Object fieldVal = classpathOrderOut.reflectionUtils.getFieldVal(false, bundlefile, + fieldName); if (fieldVal != null) { foundClassPathElement = true; // We found the base file and a classpath element, e.g. "bin/" @@ -119,8 +119,8 @@ private static void addBundleFile(final Object bundlefile, final Set pat if (bundlefile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { // Handle nested ZipBundleFile with "!/" separator - final Object baseBundleFile = ReflectionUtils.getFieldVal(false, bundlefile, - "baseBundleFile"); + final Object baseBundleFile = classpathOrderOut.reflectionUtils.getFieldVal(false, + bundlefile, "baseBundleFile"); if (baseBundleFile != null && baseBundleFile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { base = baseBundleFile; @@ -138,10 +138,10 @@ private static void addBundleFile(final Object bundlefile, final Set pat } } - addBundleFile(ReflectionUtils.getFieldVal(false, bundlefile, "wrapped"), path, classLoader, - classpathOrderOut, scanSpec, log); - addBundleFile(ReflectionUtils.getFieldVal(false, bundlefile, "next"), path, classLoader, - classpathOrderOut, scanSpec, log); + addBundleFile(classpathOrderOut.reflectionUtils.getFieldVal(false, bundlefile, "wrapped"), path, + classLoader, classpathOrderOut, scanSpec, log); + addBundleFile(classpathOrderOut.reflectionUtils.getFieldVal(false, bundlefile, "next"), path, + classLoader, classpathOrderOut, scanSpec, log); } } @@ -162,13 +162,13 @@ private static void addBundleFile(final Object bundlefile, final Set pat private static void addClasspathEntries(final Object owner, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { // type ClasspathEntry[] - final Object entries = ReflectionUtils.getFieldVal(false, owner, "entries"); + final Object entries = classpathOrderOut.reflectionUtils.getFieldVal(false, owner, "entries"); if (entries != null) { for (int i = 0, n = Array.getLength(entries); i < n; i++) { // type ClasspathEntry final Object entry = Array.get(entries, i); // type BundleFile - final Object bundlefile = ReflectionUtils.getFieldVal(false, entry, "bundlefile"); + final Object bundlefile = classpathOrderOut.reflectionUtils.getFieldVal(false, entry, "bundlefile"); addBundleFile(bundlefile, new HashSet<>(), classLoader, classpathOrderOut, scanSpec, log); } } @@ -189,11 +189,11 @@ private static void addClasspathEntries(final Object owner, final ClassLoader cl public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { // type ClasspathManager - final Object manager = ReflectionUtils.getFieldVal(false, classLoader, "manager"); + final Object manager = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "manager"); addClasspathEntries(manager, classLoader, classpathOrder, scanSpec, log); // type FragmentClasspath[] - final Object fragments = ReflectionUtils.getFieldVal(false, manager, "fragments"); + final Object fragments = classpathOrder.reflectionUtils.getFieldVal(false, manager, "fragments"); if (fragments != null) { for (int f = 0, fragLength = Array.getLength(fragments); f < fragLength; f++) { // type FragmentClasspath @@ -204,33 +204,40 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // Only read system bundles once (all bundles should give the same results for this). if (!alreadyReadSystemBundles) { // type BundleLoader - final Object delegate = ReflectionUtils.getFieldVal(false, classLoader, "delegate"); + final Object delegate = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "delegate"); // type EquinoxContainer - final Object container = ReflectionUtils.getFieldVal(false, delegate, "container"); + final Object container = classpathOrder.reflectionUtils.getFieldVal(false, delegate, "container"); // type Storage - final Object storage = ReflectionUtils.getFieldVal(false, container, "storage"); + final Object storage = classpathOrder.reflectionUtils.getFieldVal(false, container, "storage"); // type ModuleContainer - final Object moduleContainer = ReflectionUtils.getFieldVal(false, storage, "moduleContainer"); + final Object moduleContainer = classpathOrder.reflectionUtils.getFieldVal(false, storage, + "moduleContainer"); // type ModuleDatabase - final Object moduleDatabase = ReflectionUtils.getFieldVal(false, moduleContainer, "moduleDatabase"); + final Object moduleDatabase = classpathOrder.reflectionUtils.getFieldVal(false, moduleContainer, + "moduleDatabase"); // type HashMap - final Object modulesById = ReflectionUtils.getFieldVal(false, moduleDatabase, "modulesById"); + final Object modulesById = classpathOrder.reflectionUtils.getFieldVal(false, moduleDatabase, + "modulesById"); // type EquinoxSystemModule (module 0 is always the system module) - final Object module0 = ReflectionUtils.invokeMethod(false, modulesById, "get", Object.class, 0L); + final Object module0 = classpathOrder.reflectionUtils.invokeMethod(false, modulesById, "get", + Object.class, 0L); // type Bundle - final Object bundle = ReflectionUtils.invokeMethod(false, module0, "getBundle"); + final Object bundle = classpathOrder.reflectionUtils.invokeMethod(false, module0, "getBundle"); // type BundleContext - final Object bundleContext = ReflectionUtils.invokeMethod(false, bundle, "getBundleContext"); + final Object bundleContext = classpathOrder.reflectionUtils.invokeMethod(false, bundle, + "getBundleContext"); // type Bundle[] - final Object bundles = ReflectionUtils.invokeMethod(false, bundleContext, "getBundles"); + final Object bundles = classpathOrder.reflectionUtils.invokeMethod(false, bundleContext, "getBundles"); if (bundles != null) { for (int i = 0, n = Array.getLength(bundles); i < n; i++) { // type EquinoxBundle final Object equinoxBundle = Array.get(bundles, i); // type EquinoxModule - final Object module = ReflectionUtils.getFieldVal(false, equinoxBundle, "module"); + final Object module = classpathOrder.reflectionUtils.getFieldVal(false, equinoxBundle, + "module"); // type String - String location = (String) ReflectionUtils.getFieldVal(false, module, "location"); + String location = (String) classpathOrder.reflectionUtils.getFieldVal(false, module, + "location"); if (location != null) { final int fileIdx = location.indexOf("file:"); if (fileIdx >= 0) { diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java index 385e9978f..a797ce7a7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -65,9 +64,8 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, final LogNode log) { - classLoaderOrder.delegateTo( - (ClassLoader) ReflectionUtils.getFieldVal(false, classLoader, "parentContextClassLoader"), - /* isParent = */ true, log); + classLoaderOrder.delegateTo((ClassLoader) classLoaderOrder.reflectionUtils.getFieldVal(false, classLoader, + "parentContextClassLoader"), /* isParent = */ true, log); classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); classLoaderOrder.add(classLoader, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java index 243126a9c..646f71ff3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -88,85 +87,113 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final ScanSpec scanSpec, final LogNode log) { boolean valid = false; valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "getClasspath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "classpath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "classPath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "cp"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.getFieldVal(false, classLoader, "classpath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.getFieldVal(false, classLoader, "classPath"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "cp"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getPath"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "getPaths"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "path"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "paths"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "paths"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "paths"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getDir"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getDirs"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "dir"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "dirs"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "dir"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "dirs"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getFile"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject( - ReflectionUtils.invokeMethod(false, classLoader, "getFiles"), classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "file"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "files"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "file"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "files"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getJar"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getJars"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "jar"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "jars"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "jar"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "jars"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getURL"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getURLs"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getUrl"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "getUrls"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "url"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(false, classLoader, "urls"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "url"), - classLoader, scanSpec, log); - valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(false, classLoader, "urls"), - classLoader, scanSpec, log); + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getClassPath"), classLoader, + scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getClasspath"), classLoader, + scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "classpath"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "classPath"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "cp"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "classpath"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "classPath"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "cp"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getPath"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getPaths"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "path"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "paths"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "paths"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "paths"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getDir"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getDirs"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "dir"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "dirs"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "dir"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "dirs"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getFile"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getFiles"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "file"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "files"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "file"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "files"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getJar"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getJars"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "jar"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "jars"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "jar"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "jars"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getURL"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getURLs"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getUrl"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getUrls"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "url"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "urls"), classLoader, scanSpec, + log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "url"), classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( + classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "urls"), classLoader, scanSpec, log); if (log != null) { log.log("FallbackClassLoaderHandler " + (valid ? "found" : "did not find") + " classpath entries in unknown ClassLoader " + classLoader); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java index ff2c82ac6..0c9193f20 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -91,8 +91,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * the content object * @return the content location */ - private static File getContentLocation(final Object content) { - return (File) ReflectionUtils.invokeMethod(false, content, "getFile"); + private static File getContentLocation(final Object content, final ReflectionUtils reflectionUtils) { + return (File) reflectionUtils.invokeMethod(false, content, "getFile"); } /** @@ -118,21 +118,24 @@ private static void addBundle(final Object bundleWiring, final ClassLoader class bundles.add(bundleWiring); // Get the revision for this wiring - final Object revision = ReflectionUtils.invokeMethod(false, bundleWiring, "getRevision"); + final Object revision = classpathOrderOut.reflectionUtils.invokeMethod(false, bundleWiring, "getRevision"); // Get the contents - final Object content = ReflectionUtils.invokeMethod(false, revision, "getContent"); - final File location = content != null ? getContentLocation(content) : null; + final Object content = classpathOrderOut.reflectionUtils.invokeMethod(false, revision, "getContent"); + final File location = content != null ? getContentLocation(content, classpathOrderOut.reflectionUtils) + : null; if (location != null) { // Add the bundle object classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); // And any embedded content - final List embeddedContent = (List) ReflectionUtils.invokeMethod(false, revision, - "getContentPath"); + final List embeddedContent = (List) classpathOrderOut.reflectionUtils.invokeMethod(false, + revision, "getContentPath"); if (embeddedContent != null) { for (final Object embedded : embeddedContent) { if (embedded != content) { - final File embeddedLocation = embedded != null ? getContentLocation(embedded) : null; + final File embeddedLocation = embedded != null + ? getContentLocation(embedded, classpathOrderOut.reflectionUtils) + : null; if (embeddedLocation != null) { classpathOrderOut.addClasspathEntry(embeddedLocation, classLoader, scanSpec, log); } @@ -158,17 +161,18 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final ScanSpec scanSpec, final LogNode log) { // Get the wiring for the ClassLoader's bundle final Set bundles = new HashSet<>(); - final Object bundleWiring = ReflectionUtils.getFieldVal(false, classLoader, "m_wiring"); + final Object bundleWiring = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "m_wiring"); addBundle(bundleWiring, classLoader, classpathOrder, bundles, scanSpec, log); // Deal with any other bundles we might be wired to. TODO: Use the ScanSpec to narrow down the list of wires // that we follow. - final List requiredWires = (List) ReflectionUtils.invokeMethod(false, bundleWiring, + final List requiredWires = (List) classpathOrder.reflectionUtils.invokeMethod(false, bundleWiring, "getRequiredWires", String.class, null); if (requiredWires != null) { for (final Object wire : requiredWires) { - final Object provider = ReflectionUtils.invokeMethod(false, wire, "getProviderWiring"); + final Object provider = classpathOrder.reflectionUtils.invokeMethod(false, wire, + "getProviderWiring"); if (!bundles.contains(provider)) { addBundle(provider, classLoader, classpathOrder, bundles, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 6f31cd9b3..439d315b2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -40,7 +40,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -105,12 +104,13 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas return; } // PathResourceLoader has root field, which is a Path object - final Object root = ReflectionUtils.getFieldVal(false, resourceLoader, "root"); + final Object root = classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "root"); // type VirtualFile - final File physicalFile = (File) ReflectionUtils.invokeMethod(false, root, "getPhysicalFile"); + final File physicalFile = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, root, + "getPhysicalFile"); String path = null; if (physicalFile != null) { - final String name = (String) ReflectionUtils.invokeMethod(false, root, "getName"); + final String name = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getName"); if (name != null) { // getParentFile() removes "contents" directory final File file = new File(physicalFile.getParentFile(), name); @@ -124,7 +124,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas path = physicalFile.getAbsolutePath(); } } else { - path = (String) ReflectionUtils.invokeMethod(false, root, "getPathName"); + path = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPathName"); if (path == null) { // Try Path or File final File file = root instanceof Path ? ((Path) root).toFile() @@ -135,7 +135,8 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas } } if (path == null) { - final File file = (File) ReflectionUtils.getFieldVal(false, resourceLoader, "fileOfJar"); + final File file = (File) classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, + "fileOfJar"); if (file != null) { path = file.getAbsolutePath(); } @@ -172,12 +173,14 @@ private static void handleRealModule(final Object module, final Set visi // Avoid extracting paths from the same module more than once return; } - ClassLoader moduleLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, module, "getClassLoader"); + ClassLoader moduleLoader = (ClassLoader) classpathOrderOut.reflectionUtils.invokeMethod(false, module, + "getClassLoader"); if (moduleLoader == null) { moduleLoader = classLoader; } // type VFSResourceLoader[] - final Object vfsResourceLoaders = ReflectionUtils.invokeMethod(false, moduleLoader, "getResourceLoaders"); + final Object vfsResourceLoaders = classpathOrderOut.reflectionUtils.invokeMethod(false, moduleLoader, + "getResourceLoaders"); if (vfsResourceLoaders != null) { for (int i = 0, n = Array.getLength(vfsResourceLoaders); i < n; i++) { // type JarFileResourceLoader for jars, VFSResourceLoader for exploded jars, PathResourceLoader @@ -207,31 +210,34 @@ private static void handleRealModule(final Object module, final Set visi */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Object module = ReflectionUtils.invokeMethod(false, classLoader, "getModule"); - final Object callerModuleLoader = ReflectionUtils.invokeMethod(false, module, "getCallerModuleLoader"); + final Object module = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getModule"); + final Object callerModuleLoader = classpathOrder.reflectionUtils.invokeMethod(false, module, + "getCallerModuleLoader"); final Set visitedModules = new HashSet<>(); @SuppressWarnings("unchecked") - final Map moduleMap = (Map) ReflectionUtils.getFieldVal(false, - callerModuleLoader, "moduleMap"); + final Map moduleMap = (Map) classpathOrder.reflectionUtils + .getFieldVal(false, callerModuleLoader, "moduleMap"); final Set> moduleMapEntries = moduleMap != null ? moduleMap.entrySet() : Collections.> emptySet(); for (final Entry ent : moduleMapEntries) { // type FutureModule final Object val = ent.getValue(); // type Module - final Object realModule = ReflectionUtils.invokeMethod(false, val, "getModule"); + final Object realModule = classpathOrder.reflectionUtils.invokeMethod(false, val, "getModule"); handleRealModule(realModule, visitedModules, classLoader, classpathOrder, scanSpec, log); } // type Map> @SuppressWarnings("unchecked") - final Map> pathsMap = (Map>) ReflectionUtils.invokeMethod(false, module, - "getPaths"); + final Map> pathsMap = (Map>) classpathOrder.reflectionUtils + .invokeMethod(false, module, "getPaths"); for (final Entry> ent : pathsMap.entrySet()) { for (final Object /* ModuleClassLoader$1 */ localLoader : ent.getValue()) { // type ModuleClassLoader (outer class) - final Object moduleClassLoader = ReflectionUtils.getFieldVal(false, localLoader, "this$0"); + final Object moduleClassLoader = classpathOrder.reflectionUtils.getFieldVal(false, localLoader, + "this$0"); // type Module - final Object realModule = ReflectionUtils.getFieldVal(false, moduleClassLoader, "module"); + final Object realModule = classpathOrder.reflectionUtils.getFieldVal(false, moduleClassLoader, + "module"); handleRealModule(realModule, visitedModules, classLoader, classpathOrder, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java index 4086c06b8..9c6ed4590 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -32,7 +32,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -94,9 +93,9 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // However, it is possible for a Java agent to extend UCP by adding directly to the `ucp` field // (#537), and there is no way to read this field. Therefore, we need to use Narcissus to break // Java's encapsulation to read this, for this small corner case. - final Object ucpVal = ReflectionUtils.getFieldVal(false, classLoader, "ucp"); + final Object ucpVal = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "ucp"); if (ucpVal != null) { - final URL[] urls = (URL[]) ReflectionUtils.invokeMethod(false, ucpVal, "getURLs"); + final URL[] urls = (URL[]) classpathOrder.reflectionUtils.invokeMethod(false, ucpVal, "getURLs"); classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java index fb844d620..93074c222 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -32,7 +32,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -89,12 +88,16 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Object classpathManager = ReflectionUtils.invokeMethod(false, classLoader, "getClasspathManager"); - final Object[] entries = (Object[]) ReflectionUtils.getFieldVal(false, classpathManager, "entries"); + final Object classpathManager = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, + "getClasspathManager"); + final Object[] entries = (Object[]) classpathOrder.reflectionUtils.getFieldVal(false, classpathManager, + "entries"); if (entries != null) { for (final Object entry : entries) { - final Object bundleFile = ReflectionUtils.invokeMethod(false, entry, "getBundleFile"); - final File baseFile = (File) ReflectionUtils.invokeMethod(false, bundleFile, "getBaseFile"); + final Object bundleFile = classpathOrder.reflectionUtils.invokeMethod(false, entry, + "getBundleFile"); + final File baseFile = (File) classpathOrder.reflectionUtils.invokeMethod(false, bundleFile, + "getBaseFile"); if (baseFile != null) { classpathOrder.addClasspathEntry(baseFile.getPath(), classLoader, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java index c003c93a1..0cae55c23 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -84,8 +83,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final String classpath = (String) ReflectionUtils.invokeMethod(/* throwException = */ true, classLoader, - "getClasspath"); + final String classpath = (String) classpathOrder.reflectionUtils.invokeMethod(/* throwException = */ true, + classLoader, "getClasspath"); classpathOrder.addClasspathEntry(classpath, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java index b40eb9e89..9c68301b1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -66,8 +66,9 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * the ClassRealm instance * @return true if classloader uses a parent-first strategy */ - private static boolean isParentFirstStrategy(final ClassLoader classRealmInstance) { - final Object strategy = ReflectionUtils.getFieldVal(false, classRealmInstance, "strategy"); + private static boolean isParentFirstStrategy(final ClassLoader classRealmInstance, + final ReflectionUtils reflectionUtils) { + final Object strategy = reflectionUtils.getFieldVal(false, classRealmInstance, "strategy"); if (strategy != null) { final String strategyClassName = strategy.getClass().getName(); if (strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy") @@ -93,20 +94,21 @@ private static boolean isParentFirstStrategy(final ClassLoader classRealmInstanc public static void findClassLoaderOrder(final ClassLoader classRealm, final ClassLoaderOrder classLoaderOrder, final LogNode log) { // From ClassRealm#loadClassFromImport(String) -> getImportClassLoader(String) - final Object foreignImports = ReflectionUtils.getFieldVal(false, classRealm, "foreignImports"); + final Object foreignImports = classLoaderOrder.reflectionUtils.getFieldVal(false, classRealm, + "foreignImports"); if (foreignImports != null) { @SuppressWarnings("unchecked") final SortedSet foreignImportEntries = (SortedSet) foreignImports; for (final Object entry : foreignImportEntries) { - final ClassLoader foreignImportClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, - entry, "getClassLoader"); + final ClassLoader foreignImportClassLoader = (ClassLoader) classLoaderOrder.reflectionUtils + .invokeMethod(false, entry, "getClassLoader"); // Treat foreign import classloader as if it is a parent classloader classLoaderOrder.delegateTo(foreignImportClassLoader, /* isParent = */ true, log); } } // Get delegation order -- different strategies have different delegation orders - final boolean isParentFirst = isParentFirstStrategy(classRealm); + final boolean isParentFirst = isParentFirstStrategy(classRealm, classLoaderOrder.reflectionUtils); // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for self-first strategy if (!isParentFirst) { @@ -117,8 +119,8 @@ public static void findClassLoaderOrder(final ClassLoader classRealm, final Clas // From ClassRealm#loadClassFromParent -- N.B. we are ignoring parentImports, which is used to filter // a class name before deciding whether or not to call the parent classloader (so ClassGraph will be // able to load classes by name that are not imported from the parent classloader). - final ClassLoader parentClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(false, classRealm, - "getParentClassLoader"); + final ClassLoader parentClassLoader = (ClassLoader) classLoaderOrder.reflectionUtils.invokeMethod(false, + classRealm, "getParentClassLoader"); classLoaderOrder.delegateTo(parentClassLoader, /* isParent = */ true, log); classLoaderOrder.delegateTo(classRealm.getParent(), /* isParent = */ true, log); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index b2c99c12f..931c55434 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -36,7 +36,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -118,17 +117,17 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class @SuppressWarnings("unchecked") private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - for (final Object element : (Collection) ReflectionUtils.getFieldVal(false, classLoader, - "elements")) { + for (final Object element : (Collection) classpathOrder.reflectionUtils.getFieldVal(false, + classLoader, "elements")) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "file"), classLoader, - scanSpec, log); + classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, "file"), + classLoader, scanSpec, log); } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "root"), classLoader, - scanSpec, log); + classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, "root"), + classLoader, scanSpec, log); } else { - final Object rootPath = ReflectionUtils.invokeMethod(false, element, "getRoot"); + final Object rootPath = classpathOrder.reflectionUtils.invokeMethod(false, element, "getRoot"); if (rootPath instanceof Path) { classpathOrder.addClasspathEntry(rootPath, classLoader, scanSpec, log); } @@ -139,8 +138,8 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl @SuppressWarnings("unchecked") private static void findClasspathOrderForRuntimeClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final Collection applicationClassDirectories = (Collection) ReflectionUtils.getFieldVal(false, - classLoader, "applicationClassDirectories"); + final Collection applicationClassDirectories = (Collection) classpathOrder.reflectionUtils + .getFieldVal(false, classLoader, "applicationClassDirectories"); if (applicationClassDirectories != null) { for (final Path path : applicationClassDirectories) { try { @@ -158,13 +157,14 @@ private static void findClasspathOrderForRuntimeClassloader(final ClassLoader cl @SuppressWarnings("unchecked") private static void findClasspathOrderForRunnerClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - for (final Object[] elementArray : ((Map) ReflectionUtils.getFieldVal(false, classLoader, - "resourceDirectoryMap")).values()) { + for (final Object[] elementArray : ((Map) classpathOrder.reflectionUtils + .getFieldVal(false, classLoader, "resourceDirectoryMap")).values()) { for (final Object element : elementArray) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.runner.JarResource".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(false, element, "jarPath"), - classLoader, scanSpec, log); + classpathOrder.addClasspathEntry( + classpathOrder.reflectionUtils.getFieldVal(false, element, "jarPath"), classLoader, + scanSpec, log); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index a76ee7511..e4604291a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -63,8 +63,8 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * the {@link ClassLoader}. * @return true if this classloader delegates to its parent. */ - private static boolean isParentFirst(final ClassLoader classLoader) { - final Object delegateObject = ReflectionUtils.getFieldVal(false, classLoader, "delegate"); + private static boolean isParentFirst(final ClassLoader classLoader, final ReflectionUtils reflectionUtils) { + final Object delegateObject = reflectionUtils.getFieldVal(false, classLoader, "delegate"); if (delegateObject != null) { return (boolean) delegateObject; } @@ -84,7 +84,7 @@ private static boolean isParentFirst(final ClassLoader classLoader) { */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, final LogNode log) { - final boolean isParentFirst = isParentFirst(classLoader); + final boolean isParentFirst = isParentFirst(classLoader, classLoaderOrder.reflectionUtils); if (isParentFirst) { // Use parent-first delegation order classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); @@ -122,16 +122,16 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { // type StandardRoot (implements WebResourceRoot) - final Object resources = ReflectionUtils.invokeMethod(false, classLoader, "getResources"); + final Object resources = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getResources"); // type List - final Object baseURLs = ReflectionUtils.invokeMethod(false, resources, "getBaseUrls"); + final Object baseURLs = classpathOrder.reflectionUtils.invokeMethod(false, resources, "getBaseUrls"); classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); // type List> // members: preResources, mainResources, classResources, jarResources, // postResources @SuppressWarnings("unchecked") - final List> allResources = (List>) ReflectionUtils.getFieldVal(false, resources, - "allResources"); + final List> allResources = (List>) classpathOrder.reflectionUtils.getFieldVal(false, + resources, "allResources"); if (allResources != null) { // type List for (final List webResourceSetList : allResources) { @@ -141,23 +141,26 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class for (final Object webResourceSet : webResourceSetList) { if (webResourceSet != null) { // For DirResourceSet - final File file = (File) ReflectionUtils.invokeMethod(false, webResourceSet, "getFileBase"); + final File file = (File) classpathOrder.reflectionUtils.invokeMethod(false, webResourceSet, + "getFileBase"); String base = file == null ? null : file.getPath(); if (base == null) { // For FileResourceSet - base = (String) ReflectionUtils.invokeMethod(false, webResourceSet, "getBase"); + base = (String) classpathOrder.reflectionUtils.invokeMethod(false, webResourceSet, + "getBase"); } if (base == null) { // For JarResourceSet and JarWarResourceSet // The absolute path to the WAR file on the file system in which the JAR is // located - base = (String) ReflectionUtils.invokeMethod(false, webResourceSet, "getBaseUrlString"); + base = (String) classpathOrder.reflectionUtils.invokeMethod(false, webResourceSet, + "getBaseUrlString"); } if (base != null) { // For JarWarResourceSet: the path within the WAR file where the JAR file is // located - final String archivePath = (String) ReflectionUtils.getFieldVal(false, webResourceSet, - "archivePath"); + final String archivePath = (String) classpathOrder.reflectionUtils.getFieldVal(false, + webResourceSet, "archivePath"); if (archivePath != null && !archivePath.isEmpty()) { // If archivePath is non-null, this is a jar within a war base += "!" + (archivePath.startsWith("/") ? archivePath : "/" + archivePath); @@ -168,8 +171,8 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class || className.equals("java.org.apache.catalina.webresources.JarWarResourceSet"); // The path within this WebResourceSet where resources will be served from, // e.g. for a resource JAR, this would be "META-INF/resources" - final String internalPath = (String) ReflectionUtils.invokeMethod(false, webResourceSet, - "getInternalPath"); + final String internalPath = (String) classpathOrder.reflectionUtils.invokeMethod(false, + webResourceSet, "getInternalPath"); if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { classpathOrder.addClasspathEntryObject(base + (isJar ? "!" : "") + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), @@ -183,7 +186,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } } // This may or may not duplicate the above - final Object urls = ReflectionUtils.invokeMethod(false, classLoader, "getURLs"); + final Object urls = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getURLs"); classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java index cc3a7a29f..7077270a9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -87,7 +86,8 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // For Uno-Jar: - final String unoJarOneJarPath = (String) ReflectionUtils.invokeMethod(false, classLoader, "getOneJarPath"); + final String unoJarOneJarPath = (String) classpathOrder.reflectionUtils.invokeMethod(false, classLoader, + "getOneJarPath"); classpathOrder.addClasspathEntry(unoJarOneJarPath, classLoader, scanSpec, log); // If this property is defined, Uno-Jar jar path was specified on commandline. Otherwise, jar path diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java index 2443c04ef..5d00ef5a4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -90,10 +89,10 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { classpathOrder.addClasspathPathStr( // - (String) ReflectionUtils.invokeMethod(false, classLoader, "getFinderClassPath"), classLoader, - scanSpec, log); + (String) classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getFinderClassPath"), + classLoader, scanSpec, log); classpathOrder.addClasspathPathStr( // - (String) ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"), classLoader, scanSpec, - log); + (String) classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getClassPath"), + classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index e910c5318..261ca0509 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -107,7 +107,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * the containerClassLoader object * @return Collection of path objects as a {@link URL} or {@link String}. */ - private static Collection getPaths(final Object containerClassLoader) { + private static Collection getPaths(final Object containerClassLoader, + final ReflectionUtils reflectionUtils) { if (containerClassLoader == null) { return Collections.emptyList(); } @@ -115,42 +116,42 @@ private static Collection getPaths(final Object containerClassLoader) { // Expecting this to be an instance of // "com.ibm.ws.classloading.internal.ContainerClassLoader$UniversalContainer". // Call "getContainerURLs" to get its container's classpath. - Collection urls = callGetUrls(containerClassLoader, "getContainerURLs"); + Collection urls = callGetUrls(containerClassLoader, "getContainerURLs", reflectionUtils); if (urls != null && !urls.isEmpty()) { return urls; } // "getContainerURLs" didn't work, try getting the container object... - final Object container = ReflectionUtils.getFieldVal(false, containerClassLoader, "container"); + final Object container = reflectionUtils.getFieldVal(false, containerClassLoader, "container"); if (container == null) { return Collections.emptyList(); } // Should be an instance of "com.ibm.wsspi.adaptable.module.Container". // Call "getURLs" to get its classpath. - urls = callGetUrls(container, "getURLs"); + urls = callGetUrls(container, "getURLs", reflectionUtils); if (urls != null && !urls.isEmpty()) { return urls; } // "getURLs" did not work, reverting to previous logic of introspection of the "delegate". - final Object delegate = ReflectionUtils.getFieldVal(false, container, "delegate"); + final Object delegate = reflectionUtils.getFieldVal(false, container, "delegate"); if (delegate == null) { return Collections.emptyList(); } - final String path = (String) ReflectionUtils.getFieldVal(false, delegate, "path"); + final String path = (String) reflectionUtils.getFieldVal(false, delegate, "path"); if (path != null && path.length() > 0) { return Collections.singletonList((Object) path); } - final Object base = ReflectionUtils.getFieldVal(false, delegate, "base"); + final Object base = reflectionUtils.getFieldVal(false, delegate, "base"); if (base == null) { // giving up. return Collections.emptyList(); } - final Object archiveFile = ReflectionUtils.getFieldVal(false, base, "archiveFile"); + final Object archiveFile = reflectionUtils.getFieldVal(false, base, "archiveFile"); if (archiveFile != null) { final File file = (File) archiveFile; return Collections.singletonList((Object) file.getAbsolutePath()); @@ -170,10 +171,11 @@ private static Collection getPaths(final Object containerClassLoader) { * of the locations on disk that contribute to this container" */ @SuppressWarnings("unchecked") - private static Collection callGetUrls(final Object container, final String methodName) { + private static Collection callGetUrls(final Object container, final String methodName, + final ReflectionUtils reflectionUtils) { if (container != null) { try { - final Collection results = (Collection) ReflectionUtils.invokeMethod(false, + final Collection results = (Collection) reflectionUtils.invokeMethod(false, container, methodName); if (results != null && !results.isEmpty()) { final Collection allUrls = new HashSet<>(); @@ -213,16 +215,17 @@ private static Collection callGetUrls(final Object container, final Stri public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { Object smartClassPath; - final Object appLoader = ReflectionUtils.getFieldVal(false, classLoader, "appLoader"); + final Object appLoader = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "appLoader"); if (appLoader != null) { - smartClassPath = ReflectionUtils.getFieldVal(false, appLoader, "smartClassPath"); + smartClassPath = classpathOrder.reflectionUtils.getFieldVal(false, appLoader, "smartClassPath"); } else { - smartClassPath = ReflectionUtils.getFieldVal(false, classLoader, "smartClassPath"); + smartClassPath = classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "smartClassPath"); } if (smartClassPath != null) { // "com.ibm.ws.classloading.internal.ContainerClassLoader$SmartClassPath" // interface specifies a "getClassPath" to return all urls that makeup its path. - final Collection paths = callGetUrls(smartClassPath, "getClassPath"); + final Collection paths = callGetUrls(smartClassPath, "getClassPath", + classpathOrder.reflectionUtils); if (!paths.isEmpty()) { for (final Object path : paths) { classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); @@ -230,11 +233,12 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } else { // "getClassPath" didn't work... reverting to looping over "classPath" elements. @SuppressWarnings("unchecked") - final List classPathElements = (List) ReflectionUtils.getFieldVal(false, - smartClassPath, "classPath"); + final List classPathElements = (List) classpathOrder.reflectionUtils + .getFieldVal(false, smartClassPath, "classPath"); if (classPathElements != null && !classPathElements.isEmpty()) { for (final Object classPathElement : classPathElements) { - final Collection subPaths = getPaths(classPathElement); + final Collection subPaths = getPaths(classPathElement, + classpathOrder.reflectionUtils); for (final Object path : subPaths) { classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java index 7cab902eb..ea0e3c011 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -30,7 +30,6 @@ import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -89,7 +88,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - final String classpath = (String) ReflectionUtils.invokeMethod(false, classLoader, "getClassPath"); + final String classpath = (String) classpathOrder.reflectionUtils.invokeMethod(false, classLoader, + "getClassPath"); classpathOrder.addClasspathPathStr(classpath, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index 2c7ec2489..bbeb278f1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -42,11 +42,13 @@ /** A class to find the unique ordered classpath elements. */ class CallStackReader { + ReflectionUtils reflectionUtils; + /** * Constructor. */ - private CallStackReader() { - // Cannot be constructed + public CallStackReader(final ReflectionUtils reflectionUtils) { + this.reflectionUtils = reflectionUtils; } /** @@ -138,7 +140,7 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { * the log * @return The classes in the call stack. */ - static Class[] getClassContext(final LogNode log) { + Class[] getClassContext(final LogNode log) { Class[] callStack = null; // For JRE 9+, use StackWalker to get call stack. @@ -162,7 +164,7 @@ static Class[] getClassContext(final LogNode log) { // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html try { - callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + callStack = reflectionUtils.doPrivileged(new Callable[]>() { @Override public Class[] call() throws Exception { return getCallStackViaStackWalker(); @@ -177,7 +179,7 @@ public Class[] call() throws Exception { // because it will result in a reflective illegal access warning, see #663) if (VersionFinder.JAVA_MAJOR_VERSION < 9 && (callStack == null || callStack.length == 0)) { try { - callStack = ReflectionUtils.doPrivileged(new Callable[]>() { + callStack = reflectionUtils.doPrivileged(new Callable[]>() { @Override public Class[] call() throws Exception { return getCallStackViaSecurityManager(log); diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java index 4368bef07..99159fa8b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java @@ -30,6 +30,7 @@ import java.util.LinkedHashSet; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.LogNode; @@ -59,7 +60,7 @@ public ClassLoader[] getContextClassLoaders() { * @param log * The log. */ - ClassLoaderFinder(final ScanSpec scanSpec, final LogNode log) { + ClassLoaderFinder(final ScanSpec scanSpec, final ReflectionUtils reflectionUtils, final LogNode log) { LinkedHashSet classLoadersUnique; LogNode classLoadersFoundLog; if (scanSpec.overrideClassLoaders == null) { @@ -101,7 +102,7 @@ public ClassLoader[] getContextClassLoaders() { // Find classloaders for classes on callstack, in case any were missed try { - final Class[] callStack = CallStackReader.getClassContext(log); + final Class[] callStack = new CallStackReader(reflectionUtils).getClassContext(log); for (int i = callStack.length - 1; i >= 0; --i) { final ClassLoader callerClassLoader = callStack[i].getClassLoader(); if (callerClassLoader != null) { diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java index 517646b87..f9699a1b7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -41,6 +41,7 @@ import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry.ClassLoaderHandlerRegistryEntry; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.LogNode; /** A class to find all unique classloaders. */ @@ -48,6 +49,8 @@ public class ClassLoaderOrder { /** The {@link ClassLoader} order. */ private final List> classLoaderOrder = new ArrayList<>(); + public ReflectionUtils reflectionUtils; + /** * The set of all {@link ClassLoader} instances that have been added to the order so far, so that classloaders * don't get added twice. @@ -77,6 +80,10 @@ public class ClassLoaderOrder { // ------------------------------------------------------------------------------------------------------------- + public ClassLoaderOrder(final ReflectionUtils reflectionUtils) { + this.reflectionUtils = reflectionUtils; + } + /** * Get the {@link ClassLoader} order. * diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index c8791c826..8e64592c6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -115,12 +115,9 @@ public ClassGraphClassLoader getDelegateClassGraphClassLoader() { * @param log * The log. */ - public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { + public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflectionUtils, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); - // Ensure reflection driver is loaded - ReflectionUtils.loadReflectionDriver(); - // Require scanning traditional classpath if an override classloader is AppClassLoader (#639) boolean forceScanJavaClassPath = false; @@ -165,16 +162,18 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Only instantiate a module finder if requested moduleFinder = scanNonSystemModules || scanSpec.enableSystemJarsAndModules - ? new ModuleFinder(CallStackReader.getClassContext(classpathFinderLog), scanSpec, - scanNonSystemModules, /* scanSystemModules = */ scanSpec.enableSystemJarsAndModules, + ? new ModuleFinder(new CallStackReader(reflectionUtils).getClassContext(classpathFinderLog), + scanSpec, scanNonSystemModules, + /* scanSystemModules = */ scanSpec.enableSystemJarsAndModules, reflectionUtils, classpathFinderLog) : null; - classpathOrder = new ClasspathOrder(scanSpec); + classpathOrder = new ClasspathOrder(scanSpec, reflectionUtils); // Only look for environment classloaders if classpath and classloaders are not overridden final ClassLoaderFinder classLoaderFinder = scanSpec.overrideClasspath == null - && scanSpec.overrideClassLoaders == null ? new ClassLoaderFinder(scanSpec, classpathFinderLog) + && scanSpec.overrideClassLoaders == null + ? new ClassLoaderFinder(scanSpec, reflectionUtils, classpathFinderLog) : null; final ClassLoader[] contextClassLoaders = classLoaderFinder == null ? new ClassLoader[0] : classLoaderFinder.getContextClassLoaders(); @@ -245,7 +244,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Find all unique classloaders, in delegation order final LogNode classloaderOrderLog = classpathFinderLog == null ? null : classpathFinderLog.log("Finding unique classloaders in delegation order"); - final ClassLoaderOrder classLoaderOrder = new ClassLoaderOrder(); + final ClassLoaderOrder classLoaderOrder = new ClassLoaderOrder(reflectionUtils); final ClassLoader[] origClassLoaderOrder = scanSpec.overrideClassLoaders != null ? scanSpec.overrideClassLoaders.toArray(new ClassLoader[0]) : contextClassLoaders; diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 8152a7c4e..34e88a627 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -49,6 +49,7 @@ import io.github.classgraph.ClassGraph.ClasspathElementFilter; import io.github.classgraph.ClassGraph.ClasspathElementURLFilter; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -60,6 +61,8 @@ public class ClasspathOrder { /** The scan spec. */ private final ScanSpec scanSpec; + public ReflectionUtils reflectionUtils; + /** Unique classpath entries. */ private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); @@ -129,8 +132,9 @@ public String toString() { * @param scanSpec * the scan spec */ - ClasspathOrder(final ScanSpec scanSpec) { + ClasspathOrder(final ScanSpec scanSpec, final ReflectionUtils reflectionUtils) { this.scanSpec = scanSpec; + this.reflectionUtils = reflectionUtils; } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index e5467be32..8edc7599d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -54,6 +54,8 @@ public class ModuleFinder { /** If true, must forcibly scan {@code java.class.path}, since there was an anonymous module layer. */ private boolean forceScanJavaClassPath; + private final ReflectionUtils reflectionUtils; + // ------------------------------------------------------------------------------------------------------------- /** @@ -104,13 +106,13 @@ public boolean forceScanJavaClassPath() { * @param layerOrderOut * the layer order */ - private static void findLayerOrder(final Object /* ModuleLayer */ layer, + private void findLayerOrder(final Object /* ModuleLayer */ layer, final Set /* Set */ layerVisited, final Set /* Set */ parentLayers, final Deque /* Deque */ layerOrderOut) { if (layerVisited.add(layer)) { @SuppressWarnings("unchecked") - final List /* List */ parents = (List) ReflectionUtils + final List /* List */ parents = (List) reflectionUtils .invokeMethod(/* throwException = */ true, layer, "parents"); if (parents != null) { parentLayers.addAll(parents); @@ -133,7 +135,7 @@ private static void findLayerOrder(final Object /* ModuleLayer */ layer, * the log * @return the list */ - private static List findModuleRefs(final LinkedHashSet layers, final ScanSpec scanSpec, + private List findModuleRefs(final LinkedHashSet layers, final ScanSpec scanSpec, final LogNode log) { if (layers.isEmpty()) { return Collections.emptyList(); @@ -172,21 +174,21 @@ private static List findModuleRefs(final LinkedHashSet layers final Set /* Set */ addedModules = new HashSet<>(); final LinkedHashSet moduleRefOrder = new LinkedHashSet<>(); for (final Object /* ModuleLayer */ layer : layerOrderFinal) { - final Object /* Configuration */ configuration = ReflectionUtils + final Object /* Configuration */ configuration = reflectionUtils .invokeMethod(/* throwException = */ true, layer, "configuration"); if (configuration != null) { // Get ModuleReferences from layer configuration @SuppressWarnings("unchecked") - final Set /* Set */ modules = (Set) ReflectionUtils + final Set /* Set */ modules = (Set) reflectionUtils .invokeMethod(/* throwException = */ true, configuration, "modules"); if (modules != null) { final List modulesInLayer = new ArrayList<>(); for (final Object /* ResolvedModule */ module : modules) { - final Object /* ModuleReference */ moduleReference = ReflectionUtils + final Object /* ModuleReference */ moduleReference = reflectionUtils .invokeMethod(/* throwException = */ true, module, "reference"); if (moduleReference != null && addedModules.add(moduleReference)) { try { - modulesInLayer.add(new ModuleRef(moduleReference, layer)); + modulesInLayer.add(new ModuleRef(moduleReference, layer, reflectionUtils)); } catch (final IllegalArgumentException e) { if (log != null) { log.log("Exception while creating ModuleRef for module " + moduleReference, e); @@ -221,10 +223,10 @@ private List findModuleRefsFromCallstack(final Class[] callStack, final LinkedHashSet layers = new LinkedHashSet<>(); if (callStack != null) { for (final Class stackFrameClass : callStack) { - final Object /* Module */ module = ReflectionUtils.invokeMethod(/* throwException = */ false, + final Object /* Module */ module = reflectionUtils.invokeMethod(/* throwException = */ false, stackFrameClass, "getModule"); if (module != null) { - final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(/* throwException = */ true, + final Object /* ModuleLayer */ layer = reflectionUtils.invokeMethod(/* throwException = */ true, module, "getLayer"); if (layer != null) { layers.add(layer); @@ -244,7 +246,7 @@ private List findModuleRefsFromCallstack(final Class[] callStack, // Ignored } if (moduleLayerClass != null) { - final Object /* ModuleLayer */ bootLayer = ReflectionUtils + final Object /* ModuleLayer */ bootLayer = reflectionUtils .invokeStaticMethod(/* throwException = */ false, moduleLayerClass, "boot"); if (bootLayer != null) { layers.add(bootLayer); @@ -275,7 +277,9 @@ private List findModuleRefsFromCallstack(final Class[] callStack, * The log. */ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final boolean scanNonSystemModules, - final boolean scanSystemModules, final LogNode log) { + final boolean scanSystemModules, final ReflectionUtils reflectionUtils, final LogNode log) { + this.reflectionUtils = reflectionUtils; + // Get the module resolution order List allModuleRefsList = null; if (scanSpec.overrideModuleLayers == null) { diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 8ecc24c2d..302a1dc20 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -36,6 +36,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; +import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; @@ -72,6 +73,7 @@ import nonapi.io.github.classgraph.fileslice.Slice; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.recycler.Resettable; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -83,6 +85,8 @@ public class NestedJarHandler { /** The {@link ScanSpec}. */ public final ScanSpec scanSpec; + public ReflectionUtils reflectionUtils; + /** * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} for that file, used to ensure * that the {@link RandomAccessFile} and {@link FileChannel} for any given zipfile is opened only once. @@ -402,9 +406,11 @@ public RecyclableInflater newInstance() throws RuntimeException { * @param interruptionChecker * the interruption checker */ - public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker interruptionChecker) { + public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker interruptionChecker, + final ReflectionUtils reflectionUtils) { this.scanSpec = scanSpec; this.interruptionChecker = interruptionChecker; + this.reflectionUtils = reflectionUtils; } // ------------------------------------------------------------------------------------------------------------- @@ -1046,4 +1052,25 @@ public void close(final LogNode log) { } } } + + /** System.runFinalization() -- deprecated in JDK 18, so accessed by reflection. */ + private static Method runFinalizationMethod; + + public void runFinalizationMethod() { + if (runFinalizationMethod == null) { + runFinalizationMethod = reflectionUtils.staticMethodForNameOrNull("System", "runFinalization"); + } + if (runFinalizationMethod != null) { + try { + // Call System.runFinalization() (deprecated in JDK 18) + runFinalizationMethod.invoke(null); + } catch (final Throwable t) { + // Ignore + } + } + } + + public void closeDirectByteBuffer(final ByteBuffer backingByteBuffer) { + FileUtils.closeDirectByteBuffer(backingByteBuffer, reflectionUtils, /* log = */ null); + } } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java index 1821ddc86..548144d5a 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -32,7 +32,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; -import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -45,7 +44,6 @@ import nonapi.io.github.classgraph.fileslice.reader.RandomAccessByteBufferReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileChannelReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; -import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -72,10 +70,6 @@ public class FileSlice extends Slice { /** True if {@link #close} has been called. */ private final AtomicBoolean isClosed = new AtomicBoolean(); - /** System.runFinalization() -- deprecated in JDK 18, so accessed by reflection. */ - private static final Method runFinalizationMethod = ReflectionUtils.staticMethodForNameOrNull("System", - "runFinalization"); - /** * Constructor for treating a range of a file as a slice. * @@ -150,14 +144,7 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i } catch (IOException | OutOfMemoryError e) { // Try running garbage collection then try mapping the file again System.gc(); - if (runFinalizationMethod != null) { - try { - // Call System.runFinalization() (deprecated in JDK 18) - runFinalizationMethod.invoke(null); - } catch (final Throwable t) { - // Ignore - } - } + nestedJarHandler.runFinalizationMethod(); try { backingByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0L, fileLength); } catch (IOException | OutOfMemoryError e2) { @@ -308,7 +295,7 @@ public void close() { if (isTopLevelFileSlice && backingByteBuffer != null) { // Only close ByteBuffer in toplevel file slice, so that ByteBuffer is only closed once // (also duplicates of MappedByteBuffers cannot be closed by the cleaner API) - FileUtils.closeDirectByteBuffer(backingByteBuffer, /* log = */ null); + nestedJarHandler.closeDirectByteBuffer(backingByteBuffer); } backingByteBuffer = null; fileChannel = null; diff --git a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java index 0f22f0b17..1fbb1897b 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java @@ -61,6 +61,8 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TransferQueue; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; + /** * A cache of field types and associated constructors for each encountered class, used to speed up constructor * lookup. @@ -88,6 +90,8 @@ class ClassFieldCache { /** Placeholder constructor to signify no constructor was found previously. */ private static final Constructor NO_CONSTRUCTOR; + ReflectionUtils reflectionUtils; + static { try { NO_CONSTRUCTOR = NoConstructor.class.getDeclaredConstructor(); @@ -116,9 +120,11 @@ public NoConstructor() { * @param onlySerializePublicFields * Set this to true if you only want to serialize public fields (ignored for deserialization). */ - ClassFieldCache(final boolean forDeserialization, final boolean onlySerializePublicFields) { + ClassFieldCache(final boolean forDeserialization, final boolean onlySerializePublicFields, + final ReflectionUtils reflectionUtils) { this.resolveTypes = forDeserialization; this.onlySerializePublicFields = !forDeserialization && onlySerializePublicFields; + this.reflectionUtils = reflectionUtils; } /** @@ -132,8 +138,8 @@ public NoConstructor() { ClassFields get(final Class cls) { ClassFields classFields = classToClassFields.get(cls); if (classFields == null) { - classToClassFields.put(cls, - classFields = new ClassFields(cls, resolveTypes, onlySerializePublicFields, this)); + classToClassFields.put(cls, classFields = new ClassFields(cls, resolveTypes, onlySerializePublicFields, + this, reflectionUtils)); } return classFields; } @@ -204,7 +210,7 @@ Constructor getDefaultConstructorForConcreteTypeOf(final Class cls) { && (c != Object.class || cls == Object.class); c = c.getSuperclass()) { try { final Constructor defaultConstructor = c.getDeclaredConstructor(); - JSONUtils.makeAccessible(defaultConstructor); + JSONUtils.makeAccessible(defaultConstructor, reflectionUtils); // Store found constructor in cache defaultConstructorForConcreteType.put(cls, defaultConstructor); return defaultConstructor; @@ -239,7 +245,7 @@ Constructor getConstructorWithSizeHintForConcreteTypeOf(final Class cls) { && (c != Object.class || cls == Object.class); c = c.getSuperclass()) { try { final Constructor constructorWithSizeHint = c.getDeclaredConstructor(Integer.TYPE); - JSONUtils.makeAccessible(constructorWithSizeHint); + JSONUtils.makeAccessible(constructorWithSizeHint, reflectionUtils); // Store found constructor in cache constructorForConcreteTypeWithSizeHint.put(cls, constructorWithSizeHint); return constructorWithSizeHint; diff --git a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java index 56cd29abf..dae47b6c0 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -41,6 +41,7 @@ import java.util.Set; import io.github.classgraph.ScanResult; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** * The list of fields that can be (de)serialized (non-final, non-transient, non-synthetic, accessible), and their @@ -107,7 +108,7 @@ public int compare(final Field a, final Field b) { * the class field cache */ public ClassFields(final Class cls, final boolean resolveTypes, final boolean onlySerializePublicFields, - final ClassFieldCache classFieldCache) { + final ClassFieldCache classFieldCache, final ReflectionUtils reflectionUtils) { // Find declared accessible fields in all superclasses, and resolve generic types final Set visibleFieldNames = new HashSet<>(); @@ -150,7 +151,7 @@ public ClassFields(final Class cls, final boolean resolveTypes, final boolean idField = field; } - if (JSONUtils.fieldIsSerializable(field, onlySerializePublicFields)) { + if (JSONUtils.fieldIsSerializable(field, onlySerializePublicFields, reflectionUtils)) { // Resolve field type variables, if any, using the current type resolutions. This will // completely resolve some types (in the superclass), if the subclass extends a concrete // version of a generic superclass, but it will only partially resolve variables in diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java index 2b2ce173e..97a658ec5 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -40,6 +40,7 @@ import java.util.Map; import java.util.Map.Entry; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.types.ParseException; /** @@ -700,13 +701,32 @@ private static T deserializeObject(final Class expectedType, final String * @throws IllegalArgumentException * If anything goes wrong during deserialization. */ - public static T deserializeObject(final Class expectedType, final String json) - throws IllegalArgumentException { + public static T deserializeObject(final Class expectedType, final String json, + final ReflectionUtils reflectionUtils) throws IllegalArgumentException { final ClassFieldCache classFieldCache = new ClassFieldCache(/* resolveTypes = */ true, - /* onlySerializePublicFields = */ false); + /* onlySerializePublicFields = */ false, reflectionUtils); return deserializeObject(expectedType, json, classFieldCache); } + /** + * Deserialize JSON to a new object graph, with the root object of the specified expected type. Does not work + * for generic types, since it is not possible to obtain the generic type of a Class reference. + * + * @param + * The type that the JSON should conform to. + * @param expectedType + * The class reference for the type that the JSON should conform to. + * @param json + * the JSON string to deserialize. + * @return The object graph after deserialization. + * @throws IllegalArgumentException + * If anything goes wrong during deserialization. + */ + public static T deserializeObject(final Class expectedType, final String json) + throws IllegalArgumentException { + return deserializeObject(expectedType, json, new ReflectionUtils()); + } + /** * Deserialize JSON to a new object graph, with the root object of the specified expected type, and store the * root object in the named field of the given containing object. Works for generic types, since it is possible @@ -766,10 +786,29 @@ public static void deserializeToField(final Object containingObject, final Strin * @throws IllegalArgumentException * If anything goes wrong during deserialization. */ - public static void deserializeToField(final Object containingObject, final String fieldName, final String json) - throws IllegalArgumentException { + public static void deserializeToField(final Object containingObject, final String fieldName, final String json, + final ReflectionUtils reflectionUtils) throws IllegalArgumentException { final ClassFieldCache typeCache = new ClassFieldCache(/* resolveTypes = */ true, - /* onlySerializePublicFields = */ false); + /* onlySerializePublicFields = */ false, reflectionUtils); deserializeToField(containingObject, fieldName, json, typeCache); } + + /** + * Deserialize JSON to a new object graph, with the root object of the specified expected type, and store the + * root object in the named field of the given containing object. Works for generic types, since it is possible + * to obtain the generic type of a field. + * + * @param containingObject + * The object containing the named field to deserialize the object graph into. + * @param fieldName + * The name of the field to set with the result. + * @param json + * the JSON string to deserialize. + * @throws IllegalArgumentException + * If anything goes wrong during deserialization. + */ + public static void deserializeToField(final Object containingObject, final String fieldName, final String json) + throws IllegalArgumentException { + deserializeToField(containingObject, fieldName, json, new ReflectionUtils()); + } } diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java index 7a27257d3..63f23956b 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -43,6 +43,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.CollectionUtils; /** @@ -499,6 +500,27 @@ public static String serializeObject(final Object obj, final int indentWidth, return buf.toString(); } + /** + * Recursively serialize an Object (or array, list, map or set of objects) to JSON, skipping transient and final + * fields. + * + * @param obj + * The root object of the object graph to serialize. + * @param indentWidth + * If indentWidth == 0, no prettyprinting indentation is performed, otherwise this specifies the + * number of spaces to indent each level of JSON. + * @param onlySerializePublicFields + * If true, only serialize public fields. + * @return The object graph in JSON form. + * @throws IllegalArgumentException + * If anything goes wrong during serialization. + */ + public static String serializeObject(final Object obj, final int indentWidth, + final boolean onlySerializePublicFields, final ReflectionUtils reflectionUtils) { + return serializeObject(obj, indentWidth, onlySerializePublicFields, new ClassFieldCache( + /* resolveTypes = */ false, /* onlySerializePublicFields = */ false, reflectionUtils)); + } + /** * Recursively serialize an Object (or array, list, map or set of objects) to JSON, skipping transient and final * fields. @@ -516,8 +538,7 @@ public static String serializeObject(final Object obj, final int indentWidth, */ public static String serializeObject(final Object obj, final int indentWidth, final boolean onlySerializePublicFields) { - return serializeObject(obj, indentWidth, onlySerializePublicFields, - new ClassFieldCache(/* resolveTypes = */ false, /* onlySerializePublicFields = */ false)); + return serializeObject(obj, indentWidth, onlySerializePublicFields, new ReflectionUtils()); } /** @@ -562,7 +583,8 @@ public static String serializeFromField(final Object containingObject, final Str + " does not have a field named \"" + fieldName + "\""); } final Field field = fieldResolvedTypeInfo.field; - if (!JSONUtils.fieldIsSerializable(field, /* onlySerializePublicFields = */ false)) { + if (!JSONUtils.fieldIsSerializable(field, /* onlySerializePublicFields = */ false, + classFieldCache.reflectionUtils)) { throw new IllegalArgumentException("Field " + containingObject.getClass().getName() + "." + fieldName + " needs to be accessible, non-transient, and non-final"); } @@ -587,16 +609,40 @@ public static String serializeFromField(final Object containingObject, final Str * number of spaces to indent each level of JSON. * @param onlySerializePublicFields * If true, only serialize public fields. + * @param reflectionUtils + * The reflection driver. * @return The object graph in JSON form. * @throws IllegalArgumentException * If anything goes wrong during serialization. */ public static String serializeFromField(final Object containingObject, final String fieldName, - final int indentWidth, final boolean onlySerializePublicFields) { + final int indentWidth, final boolean onlySerializePublicFields, final ReflectionUtils reflectionUtils) { // Don't need to resolve types during serialization final ClassFieldCache classFieldCache = new ClassFieldCache(/* resolveTypes = */ false, - onlySerializePublicFields); + onlySerializePublicFields, reflectionUtils); return serializeFromField(containingObject, fieldName, indentWidth, onlySerializePublicFields, classFieldCache); } + + /** + * Recursively serialize the named field of an object, skipping transient and final fields. + * + * @param containingObject + * The object containing the field value to serialize. + * @param fieldName + * The name of the field to serialize. + * @param indentWidth + * If indentWidth == 0, no prettyprinting indentation is performed, otherwise this specifies the + * number of spaces to indent each level of JSON. + * @param onlySerializePublicFields + * If true, only serialize public fields. + * @return The object graph in JSON form. + * @throws IllegalArgumentException + * If anything goes wrong during serialization. + */ + public static String serializeFromField(final Object containingObject, final String fieldName, + final int indentWidth, final boolean onlySerializePublicFields) { + return serializeFromField(containingObject, fieldName, indentWidth, onlySerializePublicFields, + new ReflectionUtils()); + } } diff --git a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java index 9133653d1..beadd071a 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -101,7 +101,7 @@ private static boolean tryMakeAccessible(final AccessibleObject obj) { return false; } - public static boolean makeAccessible(final AccessibleObject obj) { + public static boolean makeAccessible(final AccessibleObject obj, final ReflectionUtils reflectionUtils) { // This reflection code is duplicated from StandardReflectionDriver, because calling // ReflectionUtils.reflectionDriver.makeAccessible(obj) does not work when called from here // (private fields can't be accessed from outside this package even after calling setAccessible(true)) @@ -109,7 +109,7 @@ public static boolean makeAccessible(final AccessibleObject obj) { return true; } try { - return ReflectionUtils.doPrivileged(new Callable() { + return reflectionUtils.doPrivileged(new Callable() { @Override public Boolean call() throws Exception { return tryMakeAccessible(obj); @@ -402,11 +402,12 @@ static Class getRawType(final Type type) { * if true, only serialize public fields * @return true if the field is serializable */ - static boolean fieldIsSerializable(final Field field, final boolean onlySerializePublicFields) { + static boolean fieldIsSerializable(final Field field, final boolean onlySerializePublicFields, + final ReflectionUtils reflectionUtils) { final int modifiers = field.getModifiers(); if ((!onlySerializePublicFields || Modifier.isPublic(modifiers)) && !Modifier.isTransient(modifiers) && !Modifier.isFinal(modifiers) && ((modifiers & 0x1000 /* synthetic */) == 0)) { - return makeAccessible(field); + return makeAccessible(field, reflectionUtils); } return false; } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java index 64bab42ff..9db34a084 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionDriver.java @@ -303,7 +303,7 @@ boolean isAccessible(final Object instance, final AccessibleObject fieldOrMethod * @throws Exception * if the field could not be found */ - private Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { + protected Field findField(final Class cls, final Object obj, final String fieldName) throws Exception { final Field field = classToClassMemberCache.get(cls, /* log = */ null).fieldNameToField.get(fieldName); if (field != null) { if (!isAccessible(obj, field)) { @@ -366,7 +366,7 @@ protected Field findInstanceField(final Object obj, final String fieldName) thro * @throws Exception * if the method could not be found. */ - private Method findMethod(final Class cls, final Object obj, final String methodName, + protected Method findMethod(final Class cls, final Object obj, final String methodName, final Class... paramTypes) throws Exception { final List methodsForName = classToClassMemberCache.get(cls, null).methodNameToMethods .get(methodName); diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 360bab76d..81a7702d4 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -40,64 +40,41 @@ /** Reflection utility methods that can be used by ClassLoaderHandlers. */ public final class ReflectionUtils { /** The reflection driver to use. */ - public static ReflectionDriver reflectionDriver; - private static Class accessControllerClass; - private static Class privilegedActionClass; - private static Method accessControllerDoPrivileged; + public ReflectionDriver reflectionDriver; + private Class accessControllerClass; + private Class privilegedActionClass; + private Method accessControllerDoPrivileged; /** Call this if you change the value of {@link ClassGraph#CIRCUMVENT_ENCAPSULATION}. */ - public static void loadReflectionDriver() { - if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NARCISSUS - && (reflectionDriver == null || !(reflectionDriver instanceof NarcissusReflectionDriver))) { + public ReflectionUtils() { + if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NARCISSUS) { try { reflectionDriver = new NarcissusReflectionDriver(); } catch (final Throwable t) { System.err.println("Could not load Narcissus reflection driver: " + t); // Fall back to standard reflection driver } - } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER - && (reflectionDriver == null || !(reflectionDriver instanceof JVMDriverReflectionDriver))) { + } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER) { try { reflectionDriver = new JVMDriverReflectionDriver(); } catch (final Throwable t) { System.err.println("Could not load JVM-Driver reflection driver: " + t); // Fall back to standard reflection driver } - } else if (reflectionDriver == null - || ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.NONE) { + } + if (reflectionDriver == null) { reflectionDriver = new StandardReflectionDriver(); } try { - if (accessControllerClass == null) { - accessControllerClass = Class.forName("java.security.AccessController"); - } - if (privilegedActionClass == null) { - privilegedActionClass = Class.forName("java.security.PrivilegedAction"); - } - if (accessControllerDoPrivileged == null) { - accessControllerDoPrivileged = accessControllerClass.getMethod("doPrivileged", - privilegedActionClass); - } + accessControllerClass = reflectionDriver.findClass("java.security.AccessController"); + privilegedActionClass = reflectionDriver.findClass("java.security.PrivilegedAction"); + accessControllerDoPrivileged = reflectionDriver.findMethod(accessControllerClass, null, "doPrivileged", + privilegedActionClass); } catch (final Throwable t) { // Ignore } } - /** Driver must be unloaded at end of scan to prevent reference being held (#756) */ - public static void unloadReflectionDriver() { - reflectionDriver = null; - accessControllerClass = null; - privilegedActionClass = null; - accessControllerDoPrivileged = null; - } - - /** - * Constructor. - */ - private ReflectionUtils() { - // Cannot be constructed - } - /** * Get the value of the field in the class of the given object or any of its superclasses. If an exception is * thrown while trying to read the field, and throwException is true, then IllegalArgumentException is thrown @@ -115,7 +92,7 @@ private ReflectionUtils() { * @throws IllegalArgumentException * If the field value could not be read. */ - public static Object getFieldVal(final boolean throwException, final Object obj, final Field field) + public Object getFieldVal(final boolean throwException, final Object obj, final Field field) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); @@ -155,7 +132,7 @@ public static Object getFieldVal(final boolean throwException, final Object obj, * @throws IllegalArgumentException * If the field value could not be read. */ - public static Object getFieldVal(final boolean throwException, final Object obj, final String fieldName) + public Object getFieldVal(final boolean throwException, final Object obj, final String fieldName) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); @@ -179,10 +156,10 @@ public static Object getFieldVal(final boolean throwException, final Object obj, } /** - * Get the value of the named static field in the given class or any of its superclasses. If an exception is - * thrown while trying to read the field value, and throwException is true, then IllegalArgumentException is - * thrown wrapping the cause, otherwise this will return null. If passed a null class reference, returns null - * unless throwException is true, then throws IllegalArgumentException. + * Get the value of the named field in the given class or any of its superclasses. If an exception is thrown + * while trying to read the field value, and throwException is true, then IllegalArgumentException is thrown + * wrapping the cause, otherwise this will return null. If passed a null class reference, returns null unless + * throwException is true, then throws IllegalArgumentException. * * @param throwException * If true, throw an exception if the field value could not be read. @@ -195,7 +172,7 @@ public static Object getFieldVal(final boolean throwException, final Object obj, * @throws IllegalArgumentException * If the field value could not be read. */ - public static Object getStaticFieldVal(final boolean throwException, final Class cls, final String fieldName) + public Object getStaticFieldVal(final boolean throwException, final Class cls, final String fieldName) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); @@ -212,7 +189,7 @@ public static Object getStaticFieldVal(final boolean throwException, final Class } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( - "Can't read static field " + cls.getName() + "." + fieldName + ": " + e); + "Can't read field " + cls.getName() + "." + fieldName + ": " + e); } } return null; @@ -235,7 +212,7 @@ public static Object getStaticFieldVal(final boolean throwException, final Class * @throws IllegalArgumentException * If the method could not be invoked. */ - public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName) + public Object invokeMethod(final boolean throwException, final Object obj, final String methodName) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); @@ -278,7 +255,7 @@ public static Object invokeMethod(final boolean throwException, final Object obj * @throws IllegalArgumentException * If the method could not be invoked. */ - public static Object invokeMethod(final boolean throwException, final Object obj, final String methodName, + public Object invokeMethod(final boolean throwException, final Object obj, final String methodName, final Class argType, final Object param) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); @@ -302,10 +279,9 @@ public static Object invokeMethod(final boolean throwException, final Object obj } /** - * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException - * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If - * passed a null class reference, returns null unless throwException is true, then throws - * IllegalArgumentException. + * Invoke the named method. If an exception is thrown while trying to call the method, and throwException is + * true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If passed + * a null class reference, returns null unless throwException is true, then throws IllegalArgumentException. * * @param throwException * Whether to throw an exception on failure. @@ -318,8 +294,8 @@ public static Object invokeMethod(final boolean throwException, final Object obj * @throws IllegalArgumentException * If the method could not be invoked. */ - public static Object invokeStaticMethod(final boolean throwException, final Class cls, - final String methodName) throws IllegalArgumentException { + public Object invokeStaticMethod(final boolean throwException, final Class cls, final String methodName) + throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); } @@ -334,18 +310,16 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas return reflectionDriver.invokeStaticMethod(reflectionDriver.findStaticMethod(cls, methodName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException( - "Static method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("method \"" + methodName + "\" could not be invoked: " + e); } return null; } } /** - * Invoke the named static method. If an exception is thrown while trying to call the method, and throwException - * is true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If - * passed a null class reference, returns null unless throwException is true, then throws - * IllegalArgumentException. + * Invoke the named method. If an exception is thrown while trying to call the method, and throwException is + * true, then IllegalArgumentException is thrown wrapping the cause, otherwise this will return null. If passed + * a null class reference, returns null unless throwException is true, then throws IllegalArgumentException. * * @param throwException * Whether to throw an exception on failure. @@ -362,8 +336,8 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas * @throws IllegalArgumentException * If the method could not be invoked. */ - public static Object invokeStaticMethod(final boolean throwException, final Class cls, - final String methodName, final Class argType, final Object param) throws IllegalArgumentException { + public Object invokeStaticMethod(final boolean throwException, final Class cls, final String methodName, + final Class argType, final Object param) throws IllegalArgumentException { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); } @@ -379,8 +353,7 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas param); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException( - "Static method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("method \"" + methodName + "\" could not be invoked: " + e); } return null; } @@ -393,7 +366,7 @@ public static Object invokeStaticMethod(final boolean throwException, final Clas * The class name to load. * @return The class of the requested name, or null if an exception was thrown while trying to load the class. */ - public static Class classForNameOrNull(final String className) { + public Class classForNameOrNull(final String className) { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); } @@ -405,13 +378,13 @@ public static Class classForNameOrNull(final String className) { } /** - * Get a static method by name, but return null if any exception is thrown. + * Get a method by name, but return null if any exception is thrown. * * @param className * The class name to load. * @return The class of the requested name, or null if an exception was thrown while trying to load the class. */ - public static Method staticMethodForNameOrNull(final String className, final String staticMethodName) { + public Method staticMethodForNameOrNull(final String className, final String staticMethodName) { if (reflectionDriver == null) { throw new RuntimeException("Cannot use reflection after ScanResult has been closed"); } @@ -424,7 +397,7 @@ public static Method staticMethodForNameOrNull(final String className, final Str // ------------------------------------------------------------------------------------------------------------- - private static class PrivilegedActionInvocationHandler implements InvocationHandler { + private class PrivilegedActionInvocationHandler implements InvocationHandler { private final Callable callable; public PrivilegedActionInvocationHandler(final Callable callable) { @@ -442,7 +415,7 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg * (AccessController is deprecated in JDK 17). */ @SuppressWarnings("unchecked") - public static T doPrivileged(final Callable callable) throws Throwable { + public T doPrivileged(final Callable callable) throws Throwable { if (accessControllerDoPrivileged != null) { final Object privilegedAction = Proxy.newProxyInstance(privilegedActionClass.getClassLoader(), new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index 24b5763e4..6e0580d0b 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -31,7 +31,9 @@ import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.concurrent.Callable; /** @@ -41,6 +43,9 @@ class StandardReflectionDriver extends ReflectionDriver { private static Method setAccessibleMethod; private static Method trySetAccessibleMethod; + private static Class accessControllerClass; + private static Class privilegedActionClass; + private static Method accessControllerDoPrivileged; static { // Find deprecated methods to remove compile-time warnings @@ -56,8 +61,48 @@ class StandardReflectionDriver extends ReflectionDriver { } catch (final Throwable t) { // Ignore } + try { + accessControllerClass = Class.forName("java.security.AccessController"); + privilegedActionClass = Class.forName("java.security.PrivilegedAction"); + accessControllerDoPrivileged = accessControllerClass.getMethod("doPrivileged", privilegedActionClass); + } catch (final Throwable t) { + // Ignore + } + } + + // ------------------------------------------------------------------------------------------------------------- + + private class PrivilegedActionInvocationHandler implements InvocationHandler { + private final Callable callable; + + public PrivilegedActionInvocationHandler(final Callable callable) { + this.callable = callable; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + return callable.call(); + } + } + + /** + * Call a method in the AccessController.doPrivileged(PrivilegedAction) context, using reflection, if possible + * (AccessController is deprecated in JDK 17). + */ + @SuppressWarnings("unchecked") + private T doPrivileged(final Callable callable) throws Throwable { + if (accessControllerDoPrivileged != null) { + final Object privilegedAction = Proxy.newProxyInstance(privilegedActionClass.getClassLoader(), + new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); + return (T) accessControllerDoPrivileged.invoke(null, privilegedAction); + } else { + // Fall back to invoking in a non-privileged context + return callable.call(); + } } + // ------------------------------------------------------------------------------------------------------------- + private static boolean tryMakeAccessible(final AccessibleObject obj) { if (trySetAccessibleMethod != null) { // JDK 9+ @@ -85,7 +130,7 @@ public boolean makeAccessible(final Object instance, final AccessibleObject obj) return true; } try { - return ReflectionUtils.doPrivileged(new Callable() { + return doPrivileged(new Callable() { @Override public Boolean call() throws Exception { return tryMakeAccessible(obj); diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index b2df9866b..73707842f 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.reflection.ReflectionUtils; import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; @@ -71,6 +72,9 @@ public final class FileUtils { /** The Unsafe object. */ private static Object theUnsafe; + /** True if class' static fields have been initialized. */ + private static AtomicBoolean initialized = new AtomicBoolean(); + /** * The current directory path (only reads the current directory once, the first time this field is accessed, so * will not reflect subsequent changes to the current directory). @@ -543,20 +547,6 @@ private static void lookupCleanMethodPrivileged() { } } - static { - try { - ReflectionUtils.doPrivileged(new Callable() { - @Override - public Void call() throws Exception { - lookupCleanMethodPrivileged(); - return null; - } - }); - } catch (final Throwable e) { - // Ignore - } - } - /** * Close a direct byte buffer (run in doPrivileged). * @@ -673,10 +663,25 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff * The log. * @return True if the byteBuffer was closed/unmapped. */ - public static boolean closeDirectByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { + public static boolean closeDirectByteBuffer(final ByteBuffer byteBuffer, final ReflectionUtils reflectionUtils, + final LogNode log) { if (byteBuffer != null && byteBuffer.isDirect()) { + if (!initialized.get()) { + try { + reflectionUtils.doPrivileged(new Callable() { + @Override + public Void call() throws Exception { + lookupCleanMethodPrivileged(); + return null; + } + }); + } catch (final Throwable e) { + throw new RuntimeException("Cannot get buffer cleaner method", e); + } + initialized.set(true); + } try { - return ReflectionUtils.doPrivileged(new Callable() { + return reflectionUtils.doPrivileged(new Callable() { @Override public Boolean call() throws Exception { return closeDirectByteBufferPrivileged(byteBuffer, log); diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java index 1285f4bca..b2d80392f 100644 --- a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -18,16 +18,16 @@ class EncapsulationCircumventionTest { @AfterEach void resetAfterEachTest() { ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; - ReflectionUtils.loadReflectionDriver(); } /** Test Narcissus. */ @Test void testNarcissus() { ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS; - ReflectionUtils.loadReflectionDriver(); - assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() - .getSimpleName()).isEqualTo("NarcissusReflectionDriver"); + final ReflectionUtils reflectionUtils = new ReflectionUtils(); + assertThat( + reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass().getSimpleName()) + .isEqualTo("NarcissusReflectionDriver"); try (ScanResult scanResult = new ClassGraph() .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() .scan()) { @@ -39,8 +39,8 @@ void testNarcissus() { @Test void testJVMDriver() { ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; - ReflectionUtils.loadReflectionDriver(); - assertThat(ReflectionUtils.getStaticFieldVal(true, ReflectionUtils.class, "reflectionDriver").getClass() + final ReflectionUtils reflectionUtils = new ReflectionUtils(); + assertThat(reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass() .getSimpleName()).isEqualTo("JVMDriverReflectionDriver"); try (ScanResult scanResult = new ClassGraph() .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index 0558e6957..0f5a4597f 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -13,6 +13,7 @@ import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; +import nonapi.io.github.classgraph.reflection.ReflectionUtils; /** * JSONSerializationTest. @@ -252,6 +253,7 @@ public void testJSON() { final H h = new H(); h.g = new G(); + ReflectionUtils reflectionUtils = new ReflectionUtils(); final String json0 = JSONSerializer.serializeFromField(h, "g", 0, false); final String expected = // diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java index ce9582c47..bb92dd328 100644 --- a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -41,7 +41,7 @@ public void testOverrideClasspathAndEnableSystemJars(@TempDir final Path tmpDir) scanSpec.overrideClasspath = Collections.singletonList(classesDir); // Act - final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new ReflectionUtils(), new LogNode()); // Assert final Set paths = new TreeSet<>(); @@ -71,7 +71,7 @@ public void testOverrideClassLoaderAndEnableSystemJars(@TempDir final Path tmpDi scanSpec.overrideClassLoaders(new URLClassLoader(new URL[] { classesDir.toUri().toURL() })); // Act - final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new ReflectionUtils(), new LogNode()); // Assert final Set paths = new TreeSet<>(); @@ -101,9 +101,8 @@ public void testOverrideClasspathAndEnableSystemModules(@TempDir final Path tmpD scanSpec.overrideClasspath = Collections. singletonList(classesDir); // Act - final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new ReflectionUtils(), new LogNode()); final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); - ReflectionUtils.unloadReflectionDriver(); // Assert assertNotNull(moduleFinder, "ModuleFinder should be non-null"); @@ -134,9 +133,8 @@ public void testOverrideClassLoaderAndEnableSystemModules(@TempDir final Path tm scanSpec.overrideClassLoaders(new URLClassLoader(new URL[] { classesDir.toUri().toURL() })); // Act - final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new LogNode()); + final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new ReflectionUtils(), new LogNode()); final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); - ReflectionUtils.unloadReflectionDriver(); // Assert assertNotNull(moduleFinder, "ModuleFinder should be non-null"); From 91f291974ff391bc845715d62aaed93db5ce36b9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Mar 2023 00:54:31 -0700 Subject: [PATCH 499/713] [maven-release-plugin] prepare release classgraph-4.8.156 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d1108f7c0..cdab659b5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.156-SNAPSHOT + 4.8.156 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.156 From 26c26ad3c82e86ad8c54d3ddb5c6d77a6e04ba0e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Mar 2023 00:54:33 -0700 Subject: [PATCH 500/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cdab659b5..6a7297f02 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.156 + 4.8.157-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.156 + classgraph-4.8.150 From a2075e398073122e1ffa8652b1db3737455e7a31 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Mar 2023 01:56:42 -0700 Subject: [PATCH 501/713] Add comments --- .../java/io/github/classgraph/Classfile.java | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 55956af8d..8b761c5d9 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1549,35 +1549,45 @@ private void readMethods() throws IOException, ClassfileFormatException { final int formalParameterIndex; final int throwsTypeIndex; if (targetType == 0x01) { + // Type parameter declaration of generic method or constructor typeParameterIndex = reader.readUnsignedByte(); boundIndex = -1; formalParameterIndex = -1; throwsTypeIndex = -1; } else if (targetType == 0x12) { + // Type in bound of type parameter declaration of generic method + // or constructor typeParameterIndex = reader.readUnsignedByte(); boundIndex = reader.readUnsignedByte(); formalParameterIndex = -1; throwsTypeIndex = -1; } else if (targetType == 0x14) { + // Return type of method, or type of newly constructed object + // (empty target) typeParameterIndex = -1; boundIndex = -1; formalParameterIndex = -1; throwsTypeIndex = -1; } else if (targetType == 0x15) { + // Receiver type of method or constructor + // (empty target) typeParameterIndex = -1; boundIndex = -1; formalParameterIndex = -1; throwsTypeIndex = -1; } else if (targetType == 0x16) { - formalParameterIndex = reader.readUnsignedByte(); + // Type in formal parameter declaration of method, constructor, + // or lambda expression typeParameterIndex = -1; boundIndex = -1; + formalParameterIndex = reader.readUnsignedByte(); throwsTypeIndex = -1; } else if (targetType == 0x17) { - throwsTypeIndex = reader.readUnsignedShort(); + // Type in throws clause of method or constructor typeParameterIndex = -1; boundIndex = -1; formalParameterIndex = -1; + throwsTypeIndex = reader.readUnsignedShort(); } else { throw new ClassfileFormatException( "Class " + className + " has unknown method type annotation target 0x" @@ -1639,9 +1649,18 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { methodTypeSignature.addRecieverTypeAnnotation(annotationInfo); } else if (targetType == 0x16) { // Type in formal parameter declaration of method, constructor, - // or lambda expression + // or lambda expression. // N.B. formal parameter indices are dodgy, because not all compilers - // index parameters the same way -- so be robust here + // index parameters the same way -- so be robust here. + // The classfile spec says "A formal_parameter_index value of i may, + // but is not required to, correspond to the i'th parameter descriptor + // in the method descriptor". Also "The formal_parameter_target item + // records that a formal parameter's type is annotated, but does not + // record the type itself. The type may be found by inspecting the + // method descriptor, although a formal_parameter_index value of 0 + // does not always indicate the first parameter descriptor in the + // method descriptor." + // What the heck, guys. final List parameterTypeSignatures = methodTypeSignature .getParameterTypeSignatures(); if (formalParameterIndex < parameterTypeSignatures.size()) { @@ -1774,14 +1793,19 @@ private void readClassAttributes() throws IOException, ClassfileFormatException final int supertypeIndex; final int boundIndex; if (targetType == 0x00) { + // Type parameter declaration of generic class or interface typeParameterIndex = reader.readUnsignedByte(); supertypeIndex = -1; boundIndex = -1; } else if (targetType == 0x10) { + // Type in extends or implements clause of class declaration (including + // the direct superclass or direct superinterface of an anonymous class + // declaration), or in extends clause of interface declaration supertypeIndex = reader.readUnsignedShort(); typeParameterIndex = -1; boundIndex = -1; } else if (targetType == 0x11) { + // Type in bound of type parameter declaration of generic class or interface typeParameterIndex = reader.readUnsignedByte(); boundIndex = reader.readUnsignedByte(); supertypeIndex = -1; @@ -1806,6 +1830,9 @@ public void decorate(final ClassTypeSignature classTypeSignature) { annotationInfo); } } else if (targetType == 0x10) { + // Type in extends or implements clause of class declaration (including + // the direct superclass or direct superinterface of an anonymous class + // declaration), or in extends clause of interface declaration if (supertypeIndex == 65535) { // Type in extends clause of class declaration classTypeSignature.getSuperclassSignature().addTypeAnnotation(typePath, From cb7370e69dc62b69944a669243d87a0e88fe20aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Mar 2023 01:57:33 -0700 Subject: [PATCH 502/713] Add comments --- .../github/classgraph/ClassRefTypeSignature.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 16019792a..35d67cd40 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -173,11 +173,18 @@ protected void addTypeAnnotation(final List typePath, final Annota int nextTypeArgIdx = -1; for (final TypePathNode typePathNode : typePath) { if (typePathNode.typePathKind == 1) { + // Annotation is deeper in a nested type + // (can handle this iteratively) numDeeperNestedLevels++; } else if (typePathNode.typePathKind == 3) { + // Annotation is on a type argument of a parameterized type + // (need to handle this recursively) nextTypeArgIdx = typePathNode.typeArgumentIdx; break; } else { + // Not valid here: + // 0 => Annotation is deeper in an array type + // 2 => Annotation is on the bound of a wildcard type argument of a parameterized type throw new IllegalArgumentException("Bad typePathKind: " + typePathNode.typePathKind); } } @@ -230,9 +237,13 @@ protected void addTypeAnnotation(final List typePath, final Annota // For type descriptors (as opposed to type signatures), typeArguments is the empty list, // so need to bounds-check nextTypeArgIdx if (nextTypeArgIdx < typeArgumentList.size()) { + // type_path_kind == 3 can be followed by type_path_kind == 2, for an annotation on the + // bound of a nested type, and this has to be handled recursively on the remaining + // part of the type path + final List remainingTypePath = typePath.subList(numDeeperNestedLevels + 1, + typePath.size()); // Add type annotation to type argument - typeArgumentList.get(nextTypeArgIdx).addTypeAnnotation( - typePath.subList(numDeeperNestedLevels + 1, typePath.size()), annotationInfo); + typeArgumentList.get(nextTypeArgIdx).addTypeAnnotation(remainingTypePath, annotationInfo); } } } From 153a2c8018b499cbb5eb5acf59ff1c8d4c055d0b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Mar 2023 01:57:57 -0700 Subject: [PATCH 503/713] Handle corrupt classfiles more robustly (#758) --- .../java/io/github/classgraph/TypeArgument.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 302d57034..8050cf9eb 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -103,11 +103,17 @@ protected void addTypeAnnotation(final List typePath, final Annota // Annotation before wildcard addTypeAnnotation(annotationInfo); } else if (typePath.size() > 0 && typePath.get(0).typePathKind == 2) { - // Annotation is on the bound of a wildcard type argument of a parameterized type - typeSignature.addTypeAnnotation(typePath.subList(1, typePath.size()), annotationInfo); + // Annotation is on the bound of a wildcard type argument of a parameterized type. + // TypeSignature can be null in a corrupt classfile (#758). + if (typeSignature != null) { + typeSignature.addTypeAnnotation(typePath.subList(1, typePath.size()), annotationInfo); + } } else { - // Annotation is on a type argument of a parameterized type - typeSignature.addTypeAnnotation(typePath, annotationInfo); + // Annotation is on a type argument of a parameterized type. + // TypeSignature can be null in a corrupt classfile (#758). + if (typeSignature != null) { + typeSignature.addTypeAnnotation(typePath, annotationInfo); + } } } From 83f55db0d2dc1d73817b2d56629988e59457a7d7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Mar 2023 01:58:49 -0700 Subject: [PATCH 504/713] [maven-release-plugin] prepare release classgraph-4.8.157 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a833e7e34..bb4217086 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.157-SNAPSHOT + 4.8.157 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.157 From 8bee00ef487f5ceb666f6aa560e40207072fa870 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 Mar 2023 01:58:51 -0700 Subject: [PATCH 505/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bb4217086..d9dd227e2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.157 + 4.8.158-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.157 + classgraph-4.8.150 From bbd6d65cae4e559b7086a319d2ea999d360f799d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 02:56:29 +0000 Subject: [PATCH 506/713] Bump maven-javadoc-plugin from 3.4.1 to 3.5.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.1...maven-javadoc-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9dd227e2..7f0fa31be 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.5.0 org.apache.maven.plugins From 52da9a0e004da2ac4ed5403d45d19dff5d37e3cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 02:56:33 +0000 Subject: [PATCH 507/713] Bump jvm-driver from 9.4.2 to 9.4.3 Bumps [jvm-driver](https://github.com/toolfactory/jvm-driver) from 9.4.2 to 9.4.3. - [Release notes](https://github.com/toolfactory/jvm-driver/releases) - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-9.4.2...jvm-driver-9.4.3) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9dd227e2..84d9dace5 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 9.4.2 + 9.4.3 true From ef9f26dc840228584e7b79b854f111ac5788c85e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 02:56:36 +0000 Subject: [PATCH 508/713] Bump maven-compiler-plugin from 3.10.1 to 3.11.0 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9dd227e2..cfb92c57a 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 org.apache.maven.plugins From b930a6baa5c72bc5f8fb5d9cf16e0165b056f676 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 02:56:34 +0000 Subject: [PATCH 509/713] Bump slf4j-jdk14 from 2.0.6 to 2.0.7 Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/commits) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9dd227e2..f8a6fdcb8 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.6 + 2.0.7 test From e2160378e5c23e69c7288b55707b08faaa36d028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 02:57:23 +0000 Subject: [PATCH 510/713] Bump maven-surefire-plugin from 3.0.0-M9 to 3.1.0 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M9 to 3.1.0. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M9...surefire-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9dd227e2..b9b6c32ad 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M9 + 3.1.0 org.codehaus.mojo From 4113c9e2475a80f0bf1bfb32d0c84c4d94c2e48c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 00:49:40 -0600 Subject: [PATCH 511/713] Fix URL handling issue (#766) --- .../classgraph/classpath/ClasspathOrder.java | 79 ++++++++---------- .../issues/issue766/ClassgraphIssue766.java | 37 ++++++++ .../issue766/ProjectWithAnnotations.iar | Bin 0 -> 44018 bytes 3 files changed, 74 insertions(+), 42 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java create mode 100644 src/test/resources/issue766/ProjectWithAnnotations.iar diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 34e88a627..bfe3f565f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -312,60 +312,55 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElementStr.isEmpty()) { return false; } - URL pathElementURL; + URL pathElementURL = null; boolean hasWildcardSuffix = false; - try { - pathElementURL = pathElement instanceof URL ? (URL) pathElement - : pathElement instanceof URI ? ((URI) pathElement).toURL() - : pathElement instanceof Path ? ((Path) pathElement).toUri().toURL() - : pathElement instanceof File ? ((File) pathElement).toURI().toURL() : null; - if (pathElementURL == null) { - // Fallback -- call toString() on the path element, then try converting to a URL - final String pathElementToStr = pathElement.toString(); - if (pathElementToStr.endsWith("/*") || pathElementToStr.endsWith("\\*")) { - hasWildcardSuffix = true; - pathElementStr = pathElementToStr.substring(0, pathElementToStr.length() - 2); - // Leave pathElementURL null, so that wildcards can be handled below - } else if (pathElementToStr.equals("*")) { - hasWildcardSuffix = true; - pathElementStr = ""; - // Leave pathElementURL null, so that wildcards can be handled below - } else { - final boolean hasJarScheme = pathElementToStr.startsWith("jar:"); - int startIdx = hasJarScheme ? 4 : 0; - final Matcher m1 = schemeMatcher.matcher(pathElementToStr.substring(startIdx)); - String scheme = ""; - if (m1.find()) { - scheme = m1.group(); - startIdx += scheme.length(); - } - final String urlStr = (pathElementToStr.contains("!/") || hasJarScheme ? "jar:" : "") - + (scheme.isEmpty() ? "file:" : scheme) - // Escape '%' characters (#255) - + pathElementToStr.substring(startIdx).replace("%", "%25"); + // Fallback -- call toString() on the path element, then try converting to a URL + if (pathElementStr.endsWith("/*") || pathElementStr.endsWith("\\*")) { + hasWildcardSuffix = true; + pathElementStr = pathElementStr.substring(0, pathElementStr.length() - 2); + // Leave pathElementURL null, so that wildcards can be handled below + } else if (pathElementStr.equals("*")) { + hasWildcardSuffix = true; + pathElementStr = ""; + // Leave pathElementURL null, so that wildcards can be handled below + } else { + final Matcher m1 = schemeMatcher.matcher(pathElementStr); + if (m1.find()) { + // Path element string is URL with scheme other than `[jar:]file:`, so need to actually + // parse URL, since the scheme may be a custom scheme + try { + pathElementURL = pathElement instanceof URL ? (URL) pathElement + : pathElement instanceof URI ? ((URI) pathElement).toURL() + : pathElement instanceof Path ? ((Path) pathElement).toUri().toURL() + : pathElement instanceof File ? ((File) pathElement).toURI().toURL() + : null; + } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e2) { + // Fall through + } + if (pathElementURL == null) { + final String urlStr = pathElementStr.replace("%", "%25"); try { pathElementURL = new URL(urlStr); } catch (final MalformedURLException e) { try { - pathElementURL = new File(pathElementToStr).toURI().toURL(); + pathElementURL = new File(urlStr).toURI().toURL(); } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e1) { - if (log != null) { - log.log("Failed to convert classpath element to URL, " - + "Try prepending \"file:\" to create a URL (" + e1 + "): " - + pathElementStr); - } // Final fallback -- try just using the raw string as a URL - pathElementURL = new URL(pathElementToStr); + try { + pathElementURL = new URL(pathElementStr); + } catch (final MalformedURLException e2) { + // Fall through + } } } } + if (pathElementURL == null) { + if (log != null) { + log.log("Failed to convert classpath element to URL: " + pathElement); + } + } } - } catch (final MalformedURLException | IllegalArgumentException | IOError | SecurityException e2) { - if (log != null) { - log.log("Cannot convert to URL (" + e2 + "): " + pathElement); - } - pathElementURL = null; } if (pathElementURL != null || pathElement instanceof URI || pathElement instanceof File || pathElement instanceof Path) { diff --git a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java new file mode 100644 index 000000000..79dfc97de --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java @@ -0,0 +1,37 @@ +package io.github.classgraph.issues.issue766; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URL; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +public class ClassgraphIssue766 { + + @Test + public void testURLs() { + final URL url = ClassgraphIssue766.class.getResource("/issue766/ProjectWithAnnotations.iar"); + + final String fileUrl = "file:" + url.getPath(); + final String jarFileUrl = "jar:file:" + url.getPath(); + final String jarUrl = "jar:///" + url.getPath(); + + assertThat(scan("javax.annotation.ManagedBean", fileUrl)).containsOnly("ch.ivyteam.test.MyManagedBean"); + assertThat(scan("javax.annotation.ManagedBean", jarFileUrl)).containsOnly("ch.ivyteam.test.MyManagedBean"); + assertThat(scan("javax.annotation.ManagedBean", jarUrl)).containsOnly("ch.ivyteam.test.MyManagedBean"); + } + + public static Set scan(final String annotation, final String url) { + final ClassGraph classGraph = new ClassGraph() + .overrideClasspath(Set.of(url)).disableNestedJarScanning().enableAnnotationInfo(); + try (ScanResult result = classGraph.scan()) { + return result.getClassesWithAnnotation(annotation).getStandardClasses().getNames().stream() + .collect(Collectors.toSet()); + } + } +} diff --git a/src/test/resources/issue766/ProjectWithAnnotations.iar b/src/test/resources/issue766/ProjectWithAnnotations.iar new file mode 100644 index 0000000000000000000000000000000000000000..2f9c288329d0f2415d1564124349b8b6e540ea07 GIT binary patch literal 44018 zcmbTeWmuJKw>C_7honeLcXu~PgLF;0yOHh&>F(|>>5}f2knR?VZ{oAo-b?rM?Du`Y zAH#Wohx5MAagB5IrMxsa1O^BQ3=BxPbgEP+QM~UvP!JFka1anU5D*YLLrZ-J2U~qd z(}H+e3ou5Mpotqb8cw*eM&6rm(5f`*VVF~&Gf8Ek*xAwohRfegzT23qFlE%pZ%Wy8 z+hAvo5%Wk)GXlk~uak8iwNVFCgp2M9EL21t}S7}>j*MnUcE@~_t z29x@18KjaTCZH~iOI0I>J zgfI=6Cs$0{QtXyZmozObtYc_W`Og0}`m&S2fz&+n56k z9Xpg|ZF(3Gc%yZFo9WlAxf60hg$>Zq(N;K$LVX>r6v@nRUcBYf7eEPiY zEQ9kVXik!{BjxQmn%}$l^GOso$E<9AJ8^RU$$%}|!LUFDYFwn3{H8^8uh<1$Dpjz) zT-HM0C0db6I%6(C5FqltuofXdvL{=x`1&HES5PwqKK4=t2?3H`_8d2HmC{qHktCN# zJv)UP7Mi3Hjb(FoT2d+!R2Xj4az>2VIaa`Drbk}c27YjG*1(oDxZCVhdiYxzc5=`V z?iS|0B7^$;19hFEyY#05Vs?Ssfw(?f>EvkvG>z#8tPL}&YNNyDAMwtI_ZykV%d(PN zZQ+&_;1k>K_~Wep-@akM_Y>U!1}T4B-FXCNVdd+=BQ{t>et>1|Pfmj7Z=z4k%9H=m zRGC>~Qs^2hq%tT9`5vZCRFaUyqk!K)i;?>>V(q*>*{OZ`lxT@l6G0iSMZqXSFY4RY z-t#oy+H{so#*f$*-+SE+?sL;_Z_wWlsr-@a?HcfKj(`URWB>@A1HjSI%-Y0(p3dCC z$kD;j#uiW!-7VM4fDklsq9oE8-xDdk3|mO#J1H=OBA$>xpBhV+xLtmHVirr@2%hfz zc)ZcYSGKe;9?q+1&p2yKQYENYUE`#$)VuPqdHBXywxoQ4@+P&dkj5Jnd`8f-b#gvt z$S*mF6~T0u@w_?kaH2hOGda3G*0kdYeT1lHweWMn0_URjY|MwU{=r8;OQ(kxohp`@ zr}&kWUHE5xOX>t;auX)Q)bjw*N$&C-2U@>Hi!5=yk$Xdh;=^Nkl|2nC6jrWH-hLt? zUT)!3q?gBc%y%i#V@l_J#gtutsN-^<(>(K44M26g|4ki+rgUb`ZjJzbtJhCDBY=aM zi8a6;Xw(2>hl;2vI6nsLz%#F8eF$xsHf#q$eXevTvZ0BGnLyd4ps5u#5d}dP?YA8G zooD+|$p>gCa(AR2*M$xHL;L5SkSfGJD8hX4IM?Zvf~B38r21@`rXo8+HGdvKDJss6 zuK;b&mP4X^wc25JDDt^qVuU}$M(>j0oLH*%yiw6O>L3twVX z4II$#iFoiEZ*rxvIBWcXzhH7HCrx8WLx1!j(K=f~=w(1A*exF;ic}X^L`cHg*PxXt z8d0g{&ny|$3Pmk2gA$ii*U$YtXX~J|{&s`$%N7aA+Z~UcFz<;P`b07r`)AnCpIMQX z!1o5Yw8BJOs&&!Jk8<@uKDwa7B6{}(H|k-iPFN$MC@4~3jk1+P7mJ5Xnz$W$Y&ZSPTj*9? z3gU!X^_c9jJJKol`R8)=oAj#5!pGgiROV{P!x8U-$0sYY(|aaq++c1BiP>U*&?@fTr2<;hn_bnyNB(~3hHl`gEVBM zvNf>EfaB}-L!$T}y=%mbf#2>ZCdglbGemIVV4a~=COIq#EWGKVio}uqaU3t6 zvEs+5zZMWQ78(%CbZ5l$?gDBqy-ng{U^R9X6y#BjvD$Q#X?M{bfZLV1IGy?-Bv%EM zxu#oM`wef-T>^s&1nZ{I2ZK zPc~Z2z_(xo3hV#+Hm_lY&dH39G0PyqA7&{`TZ@99wj1PQw=DfF#JEh zg|)sF0Dj`D^e{d2AK%KQ=JEBF38Fwjdi_=>OGBX117F|K4B!BNV;6OI;n?^c4w8xI z(My|%MpwK`UOelbQ0?6y$^?U2NQ9J=x1D>Tj zbu0otfDvf1guu^Vc_+YuURG3DkXBMoj2`$%2QvppfVCmum0gihjL1OK40Q1pb>ODm zH-HHogMH6xM}o-m$(MSQs^5oR&%oBOZO3cF1FJ}Bp;iHT7CQmu5+r$`Cjn6v?X;=R z2TV*=zeK}dqel}xKNRaOT*%p&HH|ukbawtW7Ucsgl^p#};6WcgENM;7X1F*yD}O)d z;96{24q7lFO#gPAcDRPih>b0dZu!eQ6YgBhVVRs`+~XCKpVcQB!ERsPQwfr;|AUr| z!0dWR0d5Bg2=FsdsQ&@d(3JkK088%(aB!rTb(7V%);9qd2?6whzWX%=_mY>D?iE1s zTGf(p@al7zkDnu2TA33!U5*1PiHcSDY9AHl+snjXd@x#GJrApM_ zG-_MkV2ki~#8wO$VS%la;nb>ut!>Ave`%o|2sg ztB6Xkhec&f%6wyZxD#5(iWq5?x_Qg0>eFg6dIfPyU}0D?$v76$Q58v9QLBu5%Wq1| zBYi_VvT@4+2EB=l{;1_@)h~#>kTM(uetJOdhegK)gVP~)T|JCl2~{1aGjqn?94W}% z1n&6{ckrrKJ&B*CXh4;|X59Z&>tEiP-o?z(lwRN3+Qw1e(agr$fnG%4QU5QseyDj3 zhh7Whhye+o!Qjz^EWdohSrnkDh!Ma<5O85v?^7{Ut5NDRVI*Qa2ld3*GT_?YTL&(Z}MuSv@mUDQ=}F0`lDX`l~a8E`*nV3mu=Pl6@x`KSZa;dv$XSr(J7>bl3` ziukx5$H~BSG|*|e>?a}0gPe-S`1?}(^czQJ;z`;Z?j1e)$gCl^3WIWj9EXy~TeX1O zsfz#<#FILysR83447`AKbv!4vBqbHzkpnGMD8sZXuwjejs=gttWmKer7w<#A7{0~U zk+}xz!g;(4NAe`JU{7got@>c>x0SGUZ|0lxb}qC!T;`6j{5gWL*uiX^0Gj@jQGkcRVhG*XLt5wwDw85s96OPyW~Y?5g3A3?JtP9wPPt zG)dIRxw$qnCXD-mvnW1}PM$4VPkaOyd3--N=LBmK#?|YeD$TYF!d@Vb@~67rthJlt zw?pNm&@e>>rGJ;=xSQNh9+gGJarp*j?pLhcN*0pq^F>|57i-A3isXKE2r%#x^^7ed ztTfj%ngZr%Ru3K7t&<6mr}<=aW~M;&sMteWP-o~)4XgHX=hNE5V~o$`8y30M60CliT1 zLa{?|1-7tZOc~*1ALX(-BP=?;AuPTB(o11b6?rkPslfb3RFi!p=xc_w@e#ZonNtLJ zd-!k_Ml;%%tKcts>I&ASBm4V5cqxv<^P9xKRuw1nto|TfwR+0NR`%$4P*-E2AF zEim6VsM;NZTV?e|H3@dXbNu@qSRP?%`>Oz!kuaBP zi$6+Bu=G4l*KokQ4b2cc5kNarpiQ*rP6_5y$IGcSkbd_Q)#odGI5@?&Qe5rJ0%xAm z2)}H?V>Dm8ZB}Le6`mb=bGPZ957_+t&OK=aqeK@pk2&&bw_LoKRRO}*$-UY0+*UB=%&ILffIJa zWVXAfNEUkQUC5Zqwp6-yqc2025W*gZak z6(eiy&wwI!#~VohsqZ|R>ShrJWYqt#}SYqETo(y zy&ca=!ZMoUN31c?VPaTenylfjG9JB8iH@MfnM<%P)yUjkC1* zvwjUi)FbW3@jnL3?^AW-%C?^TcCt*uk0K^RYUqn_46h6b&`d6d12BT9sVD7PjE*nC zdAUe8^H>c$x)nCFQJ(fdChC zb&{S1NIryz&4d7bg+(@-ZQ8U^g1Zv*xBCNAu6yU0YysFAcD#!AJy>`T7v{!}R88+0 z75-1uHMl{a4+;}M#k9d9W*jR$?~gEc2lNT21nQV`t+6SyvC-Wm*RJk!Q^bxnD@L7Y z$Rux{st^Dm=6ba6`7F8~^7T!Hl-K&^yX8(a|0%p4RmA8M5Sbyb$ z-129!57_VaV;nK!e+0CfG@uQ=R`LHA_VZti>oy0bEh1-6a8CjP)J>s=lY)%S9+cPe z`)(FCXuc#kl1LCaK2?^&@K(cC`Ws(QWlX4z&xM$J@NjYY967pBZg(^+z+3v8+~4cb|QLd-In36PJR3CdId( z>5$;#`!rLXMI>^F@AZWG>~2{k>}9G?jLf-mx3=c8 zvE)C`ts2V7)KVN#fN{wbTb3$M<0tD!FUXsO8!xU##$NWY{e+>>yPfB|p-b~NO5T(_ z9D|2Iy)wXILbg}Sno6cS$6?y^oAB;cXR;@b$wradG1pJW(L!DECU!%px8q$Ik(^mZ z@dI|5jvh31Lfn#k>J;Nue7zT_7JL+;rSRhmM^3eT(h(U~$5Bc3@_Sl1+YFS&pEe28 z1FWSmM62ORyM-*FedxYZL9QxK=U!Fd$QE~qX)nU#vp>tJsx|wQugaHu3|}g$2Pa6G za6LVzHD&00&Ch@CrlF#Tn1j;}mH1MOG~Ktq`s1>oKNth5f&~WG_he>%(qfl6hBUDE z4lmDt6@2};!{27*ax{hVFa(o1(2^FON@?GzjaKhsalofLAkHDfFYzjlxTDaZ(^k@Y z`Ag9}kAa*gX}uVH(FL6?6|Ny3y7@7EqvnxYNNrPZu0jK%p+aCuFLm>K>KmSO*AkP; z)(Gvvm?Q4p9eVmLi+gJEQ7MGA!bu-0pHIv0zUky{3QA;@0emkM3f^nZCr%bGGawdX zC1+McH#qW9FOjj4@bsO&B=RyeArDOQ?+s*G!*6s(Z~V?YXW#w1eW3Xy0!@kde>bE5 zVw~(N<6dJN9ZF2VmC0mJ==VxE?Q5`7R!PV((gAxZlvWP_9&T|?o3Go(*Y*Q1+4NRl ziE?NxP{A&0Xf||2;b?M^?F@;OYuyoT!SFuHH7o3uNnU*w?Tb{K;iHv7`imbZuwC6o z_UygI<>H7GXk$BEsc|OCnQklg-p5AN2bA&I)#W;^DShIf3O=_L#v>l3f}6Z=p+|pX zu$gtO)v=mb@lw7>Fg~ZirX8!GAiIyj=pPi0YlT6%1(UL9e}@6yR7)ou*_qu{(Qc-o z+ipVs@RE5Qp!NZ7A6$=x;cL?v760#g@>b3X&j9LY3MlA*=;<%Th=$9;^)O%r++}WQ zXUEK{TM>mXzGH8_0;@q}m5+}rl!84weFKhZe8G4nYXQ%2S>W0{(m(Dkv_Ct`#p`Z0 zHZNotEXNseODS8$MW85D8!#P!?>p^R!)n{I#=c#1{*6zHr2U3vw7d0Y0$gz4q~n>g zWYkd?t)?oO1ICv@swg6w+D}&uvt?XFDLkQ?$E3(%j!=Z;!RM!V8~yKSTC|6^9fAE3 z8=$;)xL^IP{J#b>^g_V;@L%zngPDa@gpHF^P>fkvl+M=L1k?s9=0~W!oH!yJE*$Wm zAxcV!C;~rSfPx1DjQt>XQ{PfRK){~lWt2rvilb@a+em-4g zWTxEzJZzdG?pYKa+c3_nG~PVU+c~Y=J8w9?>OZ>~zrJ6;d)R+?JbQRNuNsA|9D%JE zgs=XJP&I;3(u-8wja>KzrFIOxatOVC9J^uwJFDSM;{<+1|J$ZX!sPO|%~PbU)0AaB z?^~y-+h%BEa!K1~=-X!)+Gm+M=h!-CS-a*qzs&OlC(?8;2$ZyQ^esu1w2BNaD|m;o z46Ue)tZIy|>3m%`7+W_Q-!LhxS03LmoA_oi@y&AboAuPD-PET2^rnNoJKyw{Pp*beuSZHMVrm*v&Tpo&a>K9gmagyDZtgel9=`wh zdGh#lYiurgc{`Vxm8h+w@b)ddwY9%d3kzzdbqp0xw^WzxHvmHIygAk z+t^rJT3T3Gn46oMef+4drl$Jx^8Eb#w7tExy1KHwyfi&M_4Vs$Ute!qTWf1;OLKEm zQ&VGOV?#qjeSLjhZEa0;byZbWWo2bWMMZged0AOmX=!OmNl9^WaZyoGVPRoGetuqF zUT$t~PEJmCc6L@)R%T{qMtXW$YHCVKN^(-t=g*%L6B82>65?ZHV`5^WqoN`sBO@Xr z!o$KsLqkJCLV|;XgMxwr{rr4Cee%)O(Kay#0AaYhxdD;b+1grLS(%xd0*s7|3=Itp z3=H)3_4V}hbak~fHGy~z_V?S{+JJ8f{9fQU2Kf6wJw5&W`Sbq%{_gJX`uh6n>gwX+ z;_U3~`1ttn@Ni>eV{L71VPRozZf~fVGwQ=l}u&4|FE* z=%65}>Da(uLOV*zibAhLgTukUdrxq40Ho59q==xh#NbJywgiR(#-|Cl2x4UjeO=NJ zc@%R((!N^4>R^aOiaH%)w4Q4s4(2xQ(ecXKXHrZH{+^&b)VcUr%s8S1h>z9}if9sP zH|6>T*|XAjCa0B^%+(XMd-~%O87=SjzH;7y?U2#0sGfeD7ar8AJC({C)j;J+L^yJ9 zW{0HWA-eQ+3a`OF(_wyZL^PzqAYa>C@I5x7UU5z3XYCcCZWl}+FC7D+L`j$fqP z#-3BamrP$hztdE3b$HgSPi!+wyd2zAjHjn#J7llDIaa+X$1)AzI8kR25cXlp;d?(NEQ54a`r1p$zS?j@ zZhdCI%J+ddgG%WdzcR2sjZa*CZ|F~vA1r1*UA_Sq>nSAAuToqLEkt;rY}QXRNNYJs zRh@7mF{s;N4RIy=40ha&JJASNM;v@D`aNope#gJh-WUK;6!IpFco?~{$vLu$-RJ88lGxVS_z4LbK&Lud309@@&n3k?kRzRD z6i0@ol_qoB)nuE4Kl-tT*r{MNLLO7T{bPZ+-bdsRHjkJC6K!27hfQ+Xi5A(<+n4VqlRe9_tc8Q%jcwNhN3(;gjl|_vRl=mUp*nUCL$AS& z92(>XLXLTv8)Dl8g5a@kiAaRyR>q92LwJZQXs>p2L}W;*CGJO^xN6G6ZC)$-uG7u2j3k7h-<5`Ppu%`2!h| z{$&<#$%O^T1Q1D4Igv^sJ-^=r*E1ho>UZGZh6B?TT;S)w30%LYDS>yq{aS>2OP^`s z>*W>O%1+%u=Q2sb#L;T;VT4?&e6j=C9NE8m+U#~5&_d@~Q*0^V`15stQaWpZU&cj6 zzg7d#afBqB;BXu52v89wB#}*6xO%+Ylk>eJzE#TlR$25havphK^edW{amNsB$w!6h zg;JjRI2P#*%4qWA8HmkI+o}~Q=L*ud@e#F3a_Gl><1dq%H}+~IFJ8H3Mr!HHWZ`h; zqH3X;P&{RB2*gnQhJJQ69V3&EupKmWy-O=A{59<4i+lVAF^qB!I@Npp_Ul~Ng4^{U zSK|KJ{e4|oaXIy6F90g#3Me#xDCb{Qw=%%h@wM{i5N2RyU|3;Yj$18sreKg0cZ_P}7Du8wND*{rIz_Wf7d8U--oB!yY)c=r7~ zowfGm)gBf&Kr=eyB1T#So<;6rzD!QKu0S7g;h?jewYJ5?G79hD8Q~3pwk+oAX}Y@c zdZF1Mf_UE{M3@ERZ1eTBHr&e6w#HhKx|IUa4q~)oC5@X zEQ$KzTLVE|bE$jH#JlE5-Py57w5u|HxP6r%3eB5W*M}4|?r!NH(H)(Tonbk_bm$$B z8KvQHd;0L z2sRE@5fMRVVB7lDBI+&}>bJjNRw%*D( ze;N9>Ju zFh#ih@V{hz<27axkk@G|u0W+Exgj5ego>pYu=F1eVhF(5FzD>vKevA%)j1Kfys8t|Mp8`zx%ME4nZkHO@A5qIlh{?G#*ku@FoFS>M&B<=CV$2l!Ec1@Zg zbrc7u;s3ZpS6N|Oan@}eqA+o6+-LamQO*#9@&T3!SNeDfYFGTARvFpErs`!@C)xDn z0W~LmK3%ozWytO5i#VcwXd}yyKNMuFS?!+;R8S94{w^H;PPP9o97d<$dI+(DHm@qT zx_o`d$Hsi2%NnEnkXk_o88o>Zx6!F0VqHB!GNqnW#!aJEG8?mj}(ZKj;R2(R}L&q}n2|;qZ6O zpycRg2^>xSWBkF!z#zsUEW#wh%+A72XXxNipxg~?i6Zb~h5CdnTa-v?E{B@yGM%7= z_?muFV9+cxd+8(>Xu}vGr{(*x6q=PqAiv9%RoScVN9Oj9a3(_NjXqrzZlvquXDUmn z&Hm&(xFxV=B&Cy_Zz#w99Y>xmQAaU)z0lIikQ49e0ACFVTDHpc^Y2a(iT-1j@ja4M73BLZI9Ih7(CfFXJ18?==F8kf zVy0K5yGLJaJ-;3Vj$`sn9FbFzj_Fz{3nF60@TQ`qIhf>6qFJswD=}3z$}_wRqfAvR z5DTx6v2;wDO7j}idh}19uT}H>!WPSUeHgi7H9z*z<+Oo7?&}Xx`snJ`mM&-#(jJ45 zy_iW9722{l1eQ6?F^iRq)Xh==vxZw8?b`Xo8UElGZ*AlJD4XA7Cbc8}k4vESMf@XX z{&8@B86qG?!8(8tL-eIBtO*p|c2W?H?>NFbyAD*@SZeE&APZ|i2Ud+)yj;(qGzZpt zONV}TzmmPK4^G8A?H9tm_0nO=ycXn1AGoI&@H@^P(IC4Z=`WC@l-vrP=|!oFt+s$ue~jlN8rV*Q(Aed6bYoV?Mlt_q(QSY`zyS0?Swg zgnw;Cy=E7GX-de_3GlB*8ao3k2NRPRlQ5&;t9>`9ZrChyVR$2DNH@ONdeh;|t@N(UbSLE; z!EYn)&{-86vtqp$yXPKcnp!vhqCo#%=h zLLX9=q7W8*`P#)D;ndR2gRZid>0^-Ae{h?{7>+)&P{#5z8pFtXF zMKRXIbATlK{F8ZUZWW=Liyo7MBVP8BvdbpUeI|TRU5f&bo*R6#{+` zq=(odA_GnGXWbBc(5x?CyHD|X1ni~YdqHI0fu6X?TO||l>PLliZ!fn z`+Kcgn!brkU+-LF9~<`w_MqpCuS@KsJh{NcKeBgn92)6&btJ3%0^sVN-qEV<#6bpZ zUzUcu)4-wJC=DF6fL9`xUGg>E+|S=aJJKqJK*`TqeoRGES#!aMvUkQ?Mqa`-svV>$ z#IZk66}3>ONr1Ka)Vcu6D##1oKHvOC{X00=l}Y@KW}HQ*-hrnO zTVffxwfZZgYN{q2Wt>Dlu)6$UwH1h?G0$T+L0)<;dEUD+$wc3WB$G1}mrnL9skjMv zYVgvF!cd}Fu!aG7#u!qaLR_hRzoZt`kx6nNU9s`F`)?slc71BNt*bmqhJ>Bp-_oYj zG7{DkDJajvJb>WeplDU8%$ZGAdOzkvKXozY-QsepVFp$eaRjq}xE?)?YVXGfL!7uX z(Iz2^&f4F&cA8vTAuV4-AM?)5F4RuQnmNUr=hLJKGki=Oiqdg>Z-|F9SWAB~0lI4u zN4s4=xFn_o^%T93yQykww+2G+NTQ!|?m=sa9O2OT8MgE%r7Jx^#Fjt+^E;@jl8%&5 zz^xG`p`D6ET8a!kl{}74B2$^z^2y^r_Ela}YYv$m?Mzo-*BAw0IykWH*!e1 z_>`-Jp{NsBRAlXip+YJYWs#&tYhOpi=DFH89T@dkh|h#xdC5PldLOtC zK44_4WuvLG{*QH&SFIQvh;a4;H6jD~KT-nW*Hs-e!@pc6hoB(4kSH6I$S;@Kq10`? z0!#_s3q1P@R5@+t>r^ilRj$SKe!8gzBlq{E-52$&5= z3v=(?0JWWEkgEffWu4o9nl8T32~XhxG8S zYn>P0aZ&m%qg-GttRr9FiAE>0?`U#|P(4>uh=9xtbIefL7s=t0L&cG7((oJPy z1XU(3^$;8FwuJYRstbxGN5mmwyEKnRS?2espSL_#o?>Wyip}Je3qo6fG_tE#OpVDd>&Her_A`)$`*I=0&84 zW)f)+S|NF1s8Id$5m5l5G)_<&gIWTOO290&lv+Nhh7;bj6)s1{MrX*~d+BFzm7rj_ zyvcd#YnoVu1qDogvZqdSjZW<@0_T%d6WgA=&`}m)PcAJ--7CY1gV-j_wVNBZULl%C zcHXk2;K{XUm~{+ICkj+W5yqT&QOHm%#={}SYZBwh@2A5-yJ| zaaZ#>(Qv(Ge z7+yMrFM`zz?tM<9VORN!a&HgkdN24|+4~jEQO9uOcI)3hoh9z*y&sRO`I6nXMckZT z=HTfWXmE$nx5vesfyG5Hiv-;j1P+4XCNI~GgT?2{eL21cZ)17rQLwkE$B~@B`L^2Y zicmV<2#nbT&VLAYnskhXr7%65H8kL%p=$d@Q#IYP%ecCqHl)B{x#9 z*Gj+fV{Q}>s>909(6k54yJ8VZ2mEO5r?I?dATYk}Jf{0HEry&AL8Us_!hlvru)*Ij zIzQiMde`8Bz)o(Sap(6^ejbb0L03ZTbM8q%rVxWSx*;>OvYIl{DVpkKw3IfD;hjw8 z^4@bi=!PB4II3E4!YeX>>r~SF|H81 z)qfvJe%Zu-(d5^Z?O*LH!jjND+et^MN!m@v1=n?-`=VwZMfv`k%{u8^j-k!#<^FJnk~&b|n7 zBjsO!&5EqLTZ-FDQY0-nA{WS9@@un^{OPdmUc0b25qwi?FeYAJMLhOX34DJ47&2np&HzMaM#aA$)ptOqoHEywomMPTAhi8fp z-py`(xft1-iN<>Hq{Z09g_~FE5`lhobQMTGQ08D>(Vmfe_8VEgpE6<__x(rUoJ~)! ztDwMqs5|t6tf9b4qVS%Ls|!_ZWGnm3(=TH`r{*~xqNjG6z}>|`k_gwoSz)`;HE%bG9sRtF!T{q^S`gqx zNHU?iBy{7CNvfVooXrXFoI`{3ePrAlC$uTWkI33i}=HenQtO}uxsS;R}iD0FG)D<$TinhVIxPn zQEnFkdYF3nr03HC!sD`{!{J7Q0nP?{KKoYgsVchsoz&17Y%h+AcVRkGxr^RcRs4vWT&_{06LLv1mgj|s$RY0K z5GK>pQqtHXOCMjzveW!%OJ6|3bJIRF)mmW!DvJH4;|4nvd>D_2gU(g56eiwr7#0BE>&|r(|cA95?0K7w6B!&!6n-xps7{`G2l%^WDTlB%>9kH zN>E1Z&MEc8$q0Y6=Yp_WUGjl1HU|{=zhCfQ%Y33^HE{k67{EIcx)EE$?3l6+_MLQMY9L!@7!9c?&Z~r?<|G&$Yk9yB-*-J1_WqHD^|w_st^h&2QtNNa zac~1(6|kcJw_$~{aT>e68ol6G_63}ZXrjg8GyH2R@}_nFwEMLUZe|53hDf29EYwFN z9Gr8rENT@gfwLUU)x5D@n^hkM}DbQnHW&*sU)v#6&Gafx_P5mMe^#o1)I2-B)K zZyV({VCh_G7>@3Tj~jjjPpo5&~Y$#m051PG)J9cjhA2 zweqNOWp1v8>1ghfgU+4OlDxCE#&43MV;Qsr6YvrFgDeOuEqDEihjEYOgmswy= zI{4SDh*r1_#&K#5;fn9-<((xw`ntpbca#{=)ktNV5wzQ_5F7e!hRp=lNBG$}>pc=e z*sj+ue1>;@xql?;Bw*TN?Oyu|A2n}r$y5uG)L(8+S6X{aHODSBX9Gz3l2b%^yZ{^) znP&YwbUlpr!<1E;&EO(i49$2cxVo<8vdPk&Em!Ktg#@?WCugiHWTrEWVZ0ASizqvi z6`M$Lcxz%~C14c zn?ESV;3C$&4SZZO@F@o(!D9hLocH|<+TQ8IjtBOFYeBT*kNR^>GqCaxtIEhRs!nY#;Y z_UUfNovaztWNzZu?*lA~rq-6!P{@hL(q*lJIAg0;sqZZOw7ZiMZE`F=GK#~-ebN&^ z`@+(z^d``@gWqS1m3dEJ@reT0klHMTgy2wtwkyOe1oE<;@Dc61^ad>TO?3r9P)mFp z>EL}jid5`i`gC6}Iok_xQ}6x{bC|2cCj1P9H479npa3s^y+RZI`(Ea+hAX|Ikdve1 ze>2o-^w(|`1Q4Su7Dao&vndh0c3-4*AAH}gg1?pGv@!*eOOtvro=fjn_Q6U0IjWmZ}V@N2lbt=-;H<@R7GBu$dQ`*% z720yrw^>90&l%qA$v8g@0utAvcm4X)we58$4$osPPj@Ln4cbM z_{*V-2Jau}>J95*ih;;pmj!?AoBr?UWC7MrlK%)iR-yUH_#j`Yx!KXVQ~lXdlboSMofDh z-u<7B)W*ud=C>p5hVv5uW)5DjnL{#8I!(7IWMhf!HF(}tHBrXN~ex(=&&A)9a~U48`tKpnjxHoDkPCo*0Frfj)URIx-ne`y5ZDuim%S z^^l?ZM~AsOz%)) z4cJMXtEqTPeS(!?Tud%Sc)d(-pYQ~&c~d9B>g=|7oOXRQtYLi^)qnwVnflX*u7zF5 z-w%~y<4Z*z5btXh{_7^!zqe!&eMf-2nV|)67xJ$tr1HBBpPd`Rau0o#gClPBEud$s zmo0=MZo4DS+duYwIis|K6I+uU-_t^LoCX#U!lGUWj_jru&v5wVRstz-aJlosZc2rP zQse1F@Yql^pcs^uGYkPw7nhg|lH&vft?yJUv_<8{zY6mnE?_az1?VN8G!jQMUr$ns z8>^QF>mTlue8jgx8{mAS(CxOR59I8@-T&b1HkJoQ91!LI!PSbHSz7)#N~cjlq#i;H zh{=wkdn{A1{17(%O(^zOMjepR4`jqBQnI)`YpiS~)GqNU>|Xm{j^2;hE6j7sU?N0$ z)iSPpTX-W!#3SGV4mYK;f3}QlYNQ_!7c?0Jlie0|+V;8k!c)87>T^NQXjHs25spl4 zJ!a6bcV~j-4?&cH;;k@71){?MK6;Bl9`VQbST*-q&xK=u4C!7(=v)3T+^mG7mF4UD z``?H6hnuYdy4fCCryRm9NQ0~5oL>=Sb4|M@n{E}6d1JN`qRc(v!XC!UMfD9VOH>29 zFvIxswXI_%B!Cs@W(B>}8U<ve#gvh(dZoMRl0U*Dahxuy`)@6`SxGMK~p8^H6xTVr^3WUKPFyeLhT1C8EdV8Y+pXd zQpm!E!F2O%2GH1Jmc1OfEVuuW2Kb&0cD>r>G*C$YGf)|Q1AyhAN{z$N;;BoH~fLLCu-CwP&fA3jjfVa2*hGaGRx_9*# z+bBr8>tD@sbru-?7d_3ZWu;Q#$R69MRUNmjlig;5aNBX@zdLLfis;#y=i$Y}KhFrF z_q>g0%0~6@yEXOSytEGUN8l48q>WX&fM(vm#K=Mt;;ANo9Gu>Da689dRu;%8Pw0~q&c1;!cjQjTS3qlprhC`&gnS%IDhd({wW$t` z;^Jvp6-N}YNOOv}eB=>}GjR+|Q8!OlLS=3G_kgP(VwG1+LSSouU=XOr#==Dw&1!5 zef4#(SnF`Q@6dV&znGH?$DRvSlJDHx(jUsXZ;?UUU|5k7*}Z`N<;K?W6l!^1p0E+M zw5$Y z_o(hCQ$c`8ltKdjILjY$?dsj|10n&UG{DTp?9ZYs68>v^8W7|bP=0NO{d-fD2Tt)^ zZ0wDGJ5#H#|4hb7yPwrxZI&VgXtO-#)#PMmiD>Il2q;5zOMdk;Z|Ox3AMg_dioU4q7W0MNSV@L)rU7PEuqyM;k_FCVq`l~7875R7 zOMEBxEd7`gLQ`>W8&2v=tKooeRBE}!Fz#Rk(Cv;nWOtYul-!_$MPePR9-Tht%hudf9au)m93Kp ze4?5+_zWFHPK|VNmTG*q8Id)e>YoWpw<3e69=dsWB7#`Kb9!LToSEm-@VPw_(WU(F zuOmmBy-)saRI8OMaY3 zZh8ry2&d1&g4L%QtUl2%c5xD2x8qCL&Zu_qN}@{4F#a4SgOtAX`}LF8d!Z||Tg*}d z$gyJr6k$+stUk*+2)~yrHhyaJ;9sHq1v)hEa~EbSvcl%PnV{cY;|0usbyMidN4Zl9 zRg~?%W%(#3WjEc(!k*5~s4Vbr9i1cMZ`>>6NJgwZo^hMJKc?OO97R=gB_n>MZen&V zp2pj+yWMrg1CA6}(G{1*xr%3kGuxbWn<25>#E{B;?A=ZwiZ7&6 z4hmYHEJqhtT}U{m+^I&2oIL&_2k6>(d$_tTpSbh;3H8FTl42aA7lt+ZSkOfZF~<0~ zN;xtg|AyciWLPhs6(c&Kbm2T#eCD)C8Y%BL&T4m0q1g>jnvM`|;}s3ReAm>^v9o}e z(LuE_NsYkbaa}>==EG+ul}tG=kg;52tvKIety9hBs5Cv}UCAR6M|LlzjHYb+sBgVs z`vuF3duF*p*)KM{zcX`-9@ zc4)q9A18hjjWzZQP70}O4Ll_D+mj~kAhh|=f61dWoy2#_JxK>C8%^?Pq=Sd;k4GAC z&2N?$iEe8jFYM!Ze?CdU)FF-Zvin6fhSxtaTv*a)El)KITGMRhKGhV37LgvK?L2>! zgK>mq4zq)-tQpY(e1*I+M{V9ey8t@X4az!Fjh>aY>oUo7F}Bcx{)VYkxhs_U#b~2v zkQ~>OaK2rZa>>=Tika>&e$CFEWLEh!tg*+;xqIq?rITwGGbh9B)ev1$3Bi|g3Oyp_ zhC|Y2doV$4v=_;U4<+0bsFL~*geeq_)S#e}z~)kQ>hzGsW~B6wM_DJaCd6zmtcxqK zWK?56P`vwoImMKwN<&FRFW3^@F0VW?^>ReJA?heZxx9{4I^QkioVc|6tAn!y5}Rgz zk%{pw2#hI7r#7t?GEKi5uI{{pSdH{uzk<-1`|H1Zcp=6#%S9uRRxkK~bFcxzB^FoTB@15zco{ezUpeLacQ`7OU+>yfD zysbRm_zKN5+vecNy-mEE`fep9w%^nz8G6yJRCZTW9-Nm>-a9&KNt*)BOBDgo=-I#V zx`n)S|I$r8Z##Gd7rWW6N5lTEev`Ye%}tpMTGx<7I>8<4AFst8G`ktlGIy(Si>3Y} zVbk6E{-#HVNt2rzQ>=uszc)xq`;*v-CA6D`4ClJG8E*&*A=3%os%74h@g&EyMvqln zK=>dyS{S5FCf}kckM6(@`*FUw+C&kWF3hKJj%LH#ry4HNf_#K?@obS`5Kny#fCuq0 zQ@bJ3J47wMj(!Re;bgt-td_MjakrPy%ch(^c5Z+^qBy9RM}AFm#_zT_@Xeq&us1goza+ z{hl|dq4cb~GXM8icumyqmUYC@PiRC9hfK@{NLeiMZ)*` zqvPMnl{4xO$EBye7;k^7IH8Kj;N&f&8)!SP=4{SJPK5~zr^3hZsnCo_qU#@a!9UXX zTDlrppsNA;w}30Je-YGI8jIXJedut$Drs6$KxyZ*gAVLvA1~YXZdnu}pX8PhQukcPfe%jfR z><}!dNNz63NziWn?Z7YdNQRZ`PW{ssQbbA}1Z6&+(b@u>o`-{js~s%gs3Hfe(821$ zo6O)~wV&5E{q&yBv%Z9-)ekIFOc0H6fFUn;_zl82|2+lw^6~BJ%5BxopOGT+CKJ_2 ztFkG1bCM}Fd?GzHY(uGLM}LAyo*iA(ji}F27w#4&e;jk8Z1VX@^H`ygh)Vil0oV1; zw&qA8`^xJg4hpvgE>YLDFl(PB%K9=|Z%i9v_W+D7+5F7C4$s^#olm zfBw{WpIv&KPO8-MeMZ{U9z}|X0=39Gia54C8E4G*2oLogV;s4BxFu8Y{ML~}p<71A z5f$q7JC6sUGcl!PNIi0V90FzjB5utgT-30cJzTLBMg+!{$Pl>u<}l<5-cxi;A>hn@ z*Uzz8%uAx>)SPCqu2$yN_ANCM#<5HSLoZ^1Pv|oh5tZWK@{&%!@G_Y4!uL>pHOos( zZubh~wPY+I@=SWYzdy`#uf?6$;=rFsmok2|+^yN3l9uWNdy;Tj*}z~1uTRt@`#~zj zCvV>-A7gZoXWgoFv|h4_r$Ewz9t_`=lNKq^6bWh&k812aJ8ZCq*9v^Y_Yffk1+Pfv|N_>2mTj69>ug;Uu55TV=J7R z^}PGy_hA$$nV4L*k72;Z9HrRDiI+t*^nhx>v+j6< zp9TZTBllF>J129=eL6Jy10qMWxCLK2$m)ei?P1K`VOP}`ltwb6OuylhWvb2Agj8~p zlqa*A*PW)JZPI7(z9e#IzUu4?`kzeU~(r!D<3}8)|aT-Z+GKr2ak|{ap9Qieu0bYiH@Ted(aLeV}lH^5+Dy zs<+F>Rbja{0N?7>tO#wL@&oscY{!D$?pG^Q&s7xuHld*SavSzX+B4hqx2L`R`P;|Y z#lTzIQ~9B(Z?q!pw2*&{v8+PR(L4_wiEQttt!05$cZn>M2gM_;pRfv?@=Eg=8GK3^ z|2|6J)~9K&sP4}nY>M2iF=xEu5-~w6$DA9vhHO(;4rO7<5Y z62~ z78~C26&fHtegg_0C^)9BqN|7h>NnM{?ICiB6*hRn(92!sqKKq(J+E+SwCq6%K`9dE zvaO%5QeO%X5C)f6r>B4LBn9hB#7&kuzR1#xXFhYf?2W9hgbV_P>gB2Bw~wyKd>PX6EQ9tWm$FLM8naz8)s_YDfQme(B*O%&R}^6hS)llobEYq z3-YcO?34zcu!0p+vAmm8E_$5{<(53ZkJtJ$kQWNMR?g$0pBvN>IcW?OB=jea4l8*( z*~(cttnRZLv^u3G4=8XRTedn;Kdoih>et;*GWY876S}B6-O5y+tNQ%Ujj@NB<`*dW zA>RYeF2C{fif;vpZdA#{%KKc%<+#ANaLuzpP3J|f6;UrTucb`(?wt~B!GZpb zUDpMy!@YKV%Os6wYweU5*mlXQn5D7$>UrDPCk;_Pyb3Kx-&-N7^yz1Np%v<2rMwuj z#r|cOkzTLvvfauq-{{F7P}s4$%bg2drT|^OjdYpMK{{H=srhtfhF1+MxR%}G?BcOK zIyf$GvcojT3G)`5(+I-wX#+7~PV|jh+prd1mnIwT%TvX_FVcPUCG@Bx*RL72FzyJ+ zXSr;AYuzowZB#{Af)6dJ+<}w$JW>r7clh*DEA0w{x6Kpwb*DI5F0$h@ePTc*0)#fDR zV@<8?HZtn2RLXxU-*Br;T|!iGPOL;?m{P}3OSp=qM|~{0@C6w=MGS#jU{_S15##*{ z&mR%v3DQ)La>0t_+92*fn!%ECRFy9CZt!rVFSAFpJQv@%mD4^7-6qzvUV7}@L>IJo z9bCA-^EbW)-H{wR;PDwbcCS(bK^^sUb@E-_&xLh)*(M9d)atMwTxh^i{c=T#LtLBNTFJzrJc64Z&^h(=JoeE47l8C7ZipV3^vdD4I-?i7F znJtWlHqhyu(QP%$+5M1Eov}1}n zcp`8nS4~xYP)eR&DC#0!yZ4J>uZ?GPzixS)^9i5jSgO8J7#3%;Iyx-d+;l!kO99%>Pb_BA^bVs@05DdGYKNB(D&Ok-APp#~R zM#d2N=MJ@*uQ{T;4f}b?W3SrPWw9K4*AeCNRS>)HB>UN{y*q%V2wCIiC8Gl9-u8`y z_aqW5{V!6Y9x&iqrma<&*N-jU{t5oLgCD_eCPNJ_+fCEEHZYfR%qz&mtZgMO9YyBv zapzJ~mysH|89WuLXo+8rsr}B9($jcOTl9?m@sn%Iiw&%;ly53M+exA#vd31N;Hv5` zhAJaod~^GYwj*_2=DdvQY3(DbbTk4&4o#wb@xcUBT`tnsTmro&zG_-;9{F!;7SA=t zA$)MsQ9so^0SSpxQQP^J#Qqg0xfQF?(n)TmxMX8NFI@6$PiL(AcJU;pBA{2vNO*5n znmO?P?p)flVaqpZ56UYVRo$A?W}oQbDV+G=J5nJwM?<+nI^0YAKLSkRiyeZx%0GEifW@X2fVvrp!w!|W)ZqSq0V zqIbbn3V1n>Krg3njA=14f{#Jj3d*AFfS7@-WpB55r73u{SSBc&JlNXwW*51hW69a_ zY`V=|QxiPjZ=|$<`-K0>N7<(X25s$Jxl98UDJ%4E=_>l1N*-?O8c-i_8LRCmcD;F3 z*)!5Ml0rE-p7%T(Fzq?ul~PS5m~TZ#Wy9=L-Guic&)h=pkP&s%4PgvbcrD(o5}jJV zO#{Q`C)qi@?#1ZnG;GP?MV6e$s;gv!SIn1EkS*jDD9Z*7>6M<7+v=Vn7SBXLuke|F zJG(6NmNM#7Dhy`5SNLC)-ovT_qIGb{QZ3ceEE6Ds?S+jIwCUqO>fin%crpGLHH zmi{s7iJGG6;(PZ7JRj;(ZmX;plEcV2%1Cxd=3tn@Jga*Td_EL9S(4|JDBxx+T2DjO zU$oys<>+N?owB`WBg>liMs*EXPWPTRQ<_!rJ|_&WHTCs`8uz`oo<4OeryH^ax!k66 z8WC@iyP;*LmZiQe7%Lg9vi4PPYn$(7$B8?yf6MHUxab_p%WG}ekEzvo*s$R^jZJ>Y zYyj)GiWDoX-ieKA&X^ffDIRrIt}GXCrC!G3)OWRb$&IbagGSi`?}q6Ku8_mDzWxi9yK0=<*hVQ02ok8yFLEuzX;P zq?$@}-C4FM*b>eQwkn0vD+Sx0sJd9hECe~bEK?F8UB%ta`E(=;hcjStcP+n!T>E{2 zbpH6;ldhJ!zQ_U84qQi5j!3It3DTEHxUEO|NU>^Eqd>%1s{1(}(^Y%X`^MSVKM#r% z6o#F8VtT<%TlEns2TcYIjZA!0uF-$@2M+-;Aa?)VezhB8GulJk`^bP=8myr$kr9)1 z+u8VtsRZ+Ail6u4xo-u^zRM#(*|*zVh?Bt8dh)<8jaoj`F17GFRG z_rp%!9`0@+q@~Z&9$~iP2DHq4uCKeq2nb%1Us-hypiFTYWcz*H1_-ZS$o(`tX-s@<*58P4^6YH8V_}dTNrzg<+*aGVUP?tR3S&nWml97J;VLZA^OY3{e1fmH<_4h@}2<|!-J16{I;Ye z)`^G}y;bWNU6ayMmtXsz@zI@wltUlXhO_nMzj0qT#uV1}fdiFz&8aPeJjb5MY(Mh- z>Q{xzO}hpqg`;8=E)ukPSGEo@9{xGD8yoKlY+M=NR)|DId5&gm{W@#MyIcG%PotQ7 zH@oV-sV*&-`(yVs_MKqok7rZB8eK|AeIy({0L**ft5}&YHx-ddidcbig0lG5Y1w%r zL1b}u^dVvb0!~uMoh1mVd8#=x!jI!7cg?wnT7(sRPP-Fh&Nw%$S7r8ts zN1qxn+8bEBsgR_0>U*p$gPo97RI9QuJ>ms)#k_kM5=BF3Udok#?!AS&>15-0(83&a}%|#ubYhl^3B#AyL{a!%+$>Kr+2!p8Jqw{jBdcT2!Zlx_13 z(GY08^faaAH;G?T<>NHckVBlxWYT!5g>^-Or`sxiL>#7-<=bC>pSSQo&c{w%1d5}E zlQkl(qZdCXT#?+lH;KmnUHR95=z%JuQ|w=64{fI@oJ`IOFNy4*n+kvp?Mb8#yi5lj zc@E05B~{ng8)RgImsC}-+#p-V)$al_U#f!l#_`h?dktS*%hawtlVAea<|Ey zb5h-HV@GCuyCIgR-`hrdkU-QsuXrp?IQd7LsG;iVEnr2Z0xPPZW~^lhdp(`9K}=mZ zSW!I=cwMeSO}I5AlQ zJ=HjeoB7I73Zx&ETtmQKOAKA!t*$#Xk?dvzS&gB)t7Sqrq$sPZ>_G0*gU|P?4X3mY zTiO;L;r1&G@hhI9aqczL@HaQPfB$IeHJ{TGLD%yqx0Tg6RB7Vz=#5-|5@6YbZ%9_{yHIv9|H_QCbmf zTN27hfsWPUk<(Y`xa&ekQNM;{hV%;enu0j&EFB=6n0ZvXh$H;oo*S*{VsNduP!WpD z^=EjHS9_^lLLo&a%b9M!gqV3Wf2WzAUAcFz8-e3{a+jKFHKHr-I|N<6pJ^}?<8U6_ znqjLxn*2bfimabQo9L>~?=4l@-|?ursS2En6?g9?)1tHoD@v@WD#umRFGE(W>ALjH zRYmwOo(w9*ZPK)S)CBlc zh`hF#raA|8Ii1as;=tw3DHdVriLujg_EdD{Q4ZcslJ{6;2Z_6!igU4?{YLSvF;Ab^ zbwyYTL&`C^H`pD>YL1+^DVH^B$$9e@BIJV&O4G|CJtq6Nf@ipxyIFy%QuMvt^0N;P zQMc3@GC5Y=-^V&l8h?7i9Jll4%Xiw84T{tn=ehU95m7(HB+?Q%yevP^b-khQMRKCW z-n!m8U(|c^&Rwq-?u@H+`db=>CI$TtvyvB+RA~s?UM@)%31(BtDk^fOk|mmZ{K^-0 zjOtfUNK*$LQ3Yi)D7GL7pS6{boe#gQwfy|8>5bCs%@4J?Rrz?Zz+F~fxJN`wM;YYB z(rjs0t8lzdfWA%D z@FpQ7?j~YmhZN-DOPQzE&mIKw-pq&i@Bz=z)6&3Vc=?Cl@+`y*?A-l`x1~e1y{%0E z!wXz|cN=J7Rom71_?6W(-oAy~V8U5VMhKpb4+E~x0~;WnQcOWQ>A{&vO_udX}JQ2M^Krf+;5*=g&eDjt1Zxm3Z#G0=Q= zn!v3J>&S*-#s0wlI7T_3Z%D6wzk)`Ee@II0b+~G-&W;;)v&V0eRDF(U_sr=faS$Ji zI>eYb8Fy_Y4(F!(Vb*7gC=|tC9UL<&pziWDebMtu;!{LOLHhp68~y?U=%O=OZCbpiH?uI zp4xdvw|ERx-08-|%HEZ3%Ne04tr=!i4;PY3R%F#?v=J3O?8+L^ejB=R0N*hl_^#(a zh*km25F21X3*-qoKOd09&Btduh;g|1p9rC;hSXZBjGSfHsI21$4o3fopj&~E2QSQa zb#Q+tu-;=?SJYATbGJdD8|7~^HrNYPWg>6xLxO>+cD?<;dn~^1Ws5>as$ismP zDtIjTx$rjruNS3Vt&cDjLppc_DeVb8b#d+G?LBPLhUu6Km>;_?h|efznu^a*zlA2x z^6YqsW2}$2&Hq1{@p3^-Ey43!qjaRla zSmr;ubS(H)Pi7d1ZlSRKL4R9E(C2rz45_&&>Oa%^_L~WkB#rUKVmSn`I}Kd%?Vk=N z^G%hw9PuZpEoj=5f7}AIvnj;5`jtlnRp#*ZE4TADy&uCEJCA0wu9WD8_jzU#f^|0p z6lBu?ZM8r)j`{rnEC%au97wzUVl-VqR^f(^zvx#gq!RI|MqC8$Lp}fB6 z)`z%ja)E-Z-~M)pR0Pssf-I3ssWH`MH86?_x_bQTij(T?ZEC7~{GFQ?#U&lV9+82! z-iUnCw@I0|r9@}o?dw%CNIBUd+~O=)a}6C&m%CK11B|H zzWFHaOconV9dd7SGmPw*CiL1j%+)(;aBM?KQu=L-C=FK5{lfbZ5ee0Ix$=@HChhY= z^GPEE&mZE?8JCx1DWSUkeMGYb(xQV-{+V$|Xu~yn?CNB_Y9hpCSbsPK{@f50B(5>2 z1}4B^CyQ&{Ex*Y3}mBc)NIjGV?8+`4WtnT z@V_Y+YXkYk*UpyT*~)vKb4TWtIZ_}F`6qjO8Y|0z0ZYMeQd>*%FTYC@B_uq}P8}&& zBKW-~OmghlO!d2;duEQ-RBt#l`lIHTbi>_GPp8|RSUt>cMb^EnF&@FDzWUGBv*G)_ zZY9l2j06K46pvn^xM_6sP*iAaPzFo5SYTLUHht3Q2>#iggg2=VUR$4drE)+}fJk}I z>4Q^uUl;!h`+a5;k@3hYaVLuABTqfE&vKj%df2FZuv6RbDgF7to?kpxp}NJvEW2jI zr#=t-xUa?ag!dAPV^p05H**A05`-;v#Gc4xgH#;#sWBwESdc2Ktw(y}jZI$qY z;|BgwzMgM#%@l6z`3_6n2~>lxmD6wfORArG1OvnQ4zBIgcOL z=_$L^Hr@C2NV@WN81;5}iBU2JE65aSm@3X&ET+>khS~Ipsqq6|p+IpoRtw7|Ifx(CIBHx(a zDHF16xgUG2`^3zQg-6sjj&IPoc0zZ2^L}YDUZHP}M*9i_zKXhA+KLFD6&%nwkcVgJ zEGJHEk|`S~vpJ5sCVvz^9Ph@~vy@mo4+HTSdi(9Oq}d-X%IpOb7@vR+Sw2Zg@sK#0 zXLrO0v4RE10ZhV$iM@PMFXU(SwDNU?>m`R$#3QL*(})3Tc@;b*@LxgJb`NJe8(+9q z(d?dW{ot?1!QUWDIQn@HUIcz0J6~U*Y4Q;OX~JxM!K>_P2NS-GN;xAY%lv?VEXU~Q zC+Go6uyN#H%;t;ylOLSqoE$)Ip@nqvFceNnk3wG(Er;SxP!AK~-0&#zD)_?CW-ceL zl_?(P!pYH5?(Yr%z+D`cHB+U-kAl;eqmOC=+x}m>u*Rdnh6O(aP9lyzBp1x8bsn-J zr8rE5lYygDQL^=@>!b#U9|33jMjui0KYRp|_Zz0e8NX3FHF!x^=KqH4E393B}PkIJpGMnF7b!WsSp`Y65N<9F!g< zQu41R;%F8e!ySdN19zrn&5~ayqC4C&I2$e6Ll&bDC$SX}Tmg*XR>3iGQJ|5;Dj<$V!C_-j#xcni3>@|0P_1Zv zMX81Q;QO8r+zPiIj-iUy99gQla482@gyWT>70dT5JaT39Qn)@Gv%7h;W$HYV*r?T&V_Bb4)2?vlwA8>L%c))@a=9=Lo;ikgz8c~wb zfj^}6qc_4xI8q{t6qdn>gySZ{P&hy#3Z<6)D-;eI0>j}zfGC_;4mTVM3kXBu0DLI) z_1~eZUA$pD9On+jKa71yV`HK08Qko@ysWH+Wgl9~cM+x_w3jniWpL>-3 zz!WEYZ4Z1H4STnv=n6BOXq>+8Fdg(W>Il*4G)VH-uKSl~pjcHf4vuzNI$ z4Y9RGRYUFiu7Lt$TA6e?x&SExMfc?;8F zpH`GEV2hi+(!&*I!cL?pbF&>TCXQ7ScGyHwpHUQYo>^%$54Q;RkVG*}_J3N0U{)Kd z!zkDx5k+M=tU}?k4u_$z>mLd|=ePp;uRz$(4h4EU{Q<7!bqBW$_HRQ$^3H!iIK16p z7VJodvN&D-u-0-bgF&$077D_1g+TxMg~2XTC~_1<{>5PmZYJ!DgfhF`{xox4&m@=# zyZxZV8eqD)C^YD-=L7^3VYeETSmg1CxZ24E#=s6ODCVdqidnksJf zZz=4tfnvD5S782ie!%V#D3HSE4-l8~6n0}kD`zaLyte)n_836xdi$>BKLCHBu+cwS z|Kno)f4c%+ashz(ut7h{_wmDR-Vak?>wc6{1SpHP{%|Gaj|dk360uN6KimA*G&u*S z{zZHB^-$pEZP~|oZ4_+1z8;Wn84eiotN^06?CTK_TS)ZNxq`5!W&3(CHk1T@qdv0y ztHNdHz8();&@TJvtl+^`?=T0pdPhM~fU~v@Jj{TN*HH%9(bcBxaP8$*9Ehz06s#Vibg&3jSK_ET#b z`yv1qLSWnkKC8`g85iqQ}5UE&2JUC6L@frKoKYnkXSk?`=!gGLrf%MEq` ba}8r%EmdM*numB*gyMOF>vrd0 Date: Mon, 8 May 2023 00:49:46 -0600 Subject: [PATCH 512/713] Code cleanup --- .../classgraph/features/EncapsulationCircumventionTest.java | 5 +++-- .../io/github/classgraph/json/JSONSerializationTest.java | 2 +- .../io/github/classgraph/classpath/ClasspathFinderTest.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java index b2d80392f..aeb62c19a 100644 --- a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -40,8 +40,9 @@ void testNarcissus() { void testJVMDriver() { ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; final ReflectionUtils reflectionUtils = new ReflectionUtils(); - assertThat(reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass() - .getSimpleName()).isEqualTo("JVMDriverReflectionDriver"); + assertThat( + reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass().getSimpleName()) + .isEqualTo("JVMDriverReflectionDriver"); try (ScanResult scanResult = new ClassGraph() .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() .scan()) { diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index 0f5a4597f..5f0e2a986 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -253,7 +253,7 @@ public void testJSON() { final H h = new H(); h.g = new G(); - ReflectionUtils reflectionUtils = new ReflectionUtils(); + final ReflectionUtils reflectionUtils = new ReflectionUtils(); final String json0 = JSONSerializer.serializeFromField(h, "g", 0, false); final String expected = // diff --git a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java index bb92dd328..f1abfe7d8 100644 --- a/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java +++ b/src/test/java/nonapi/io/github/classgraph/classpath/ClasspathFinderTest.java @@ -103,7 +103,7 @@ public void testOverrideClasspathAndEnableSystemModules(@TempDir final Path tmpD // Act final ClasspathFinder classpathFinder = new ClasspathFinder(scanSpec, new ReflectionUtils(), new LogNode()); final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); - + // Assert assertNotNull(moduleFinder, "ModuleFinder should be non-null"); assertTrue(moduleFinder.getSystemModuleRefs().size() > 0, "ModuleFinder should have found system modules"); From 94d33a27a396dce3119f9de2c72250c33d7ca297 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 00:51:20 -0600 Subject: [PATCH 513/713] Java 7 compat fix --- .../github/classgraph/issues/issue766/ClassgraphIssue766.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java index 79dfc97de..ef3ee2be4 100644 --- a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java +++ b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java @@ -26,9 +26,9 @@ public void testURLs() { assertThat(scan("javax.annotation.ManagedBean", jarUrl)).containsOnly("ch.ivyteam.test.MyManagedBean"); } - public static Set scan(final String annotation, final String url) { + public static Set scan(final String annotation, final String urlStr) { final ClassGraph classGraph = new ClassGraph() - .overrideClasspath(Set.of(url)).disableNestedJarScanning().enableAnnotationInfo(); + .overrideClasspath(Set. of(urlStr)).disableNestedJarScanning().enableAnnotationInfo(); try (ScanResult result = classGraph.scan()) { return result.getClassesWithAnnotation(annotation).getStandardClasses().getNames().stream() .collect(Collectors.toSet()); From 2b8ecb9ad96d2191560f3499765bc060422b3396 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 00:52:19 -0600 Subject: [PATCH 514/713] Java 7 compat fix --- .../github/classgraph/issues/issue766/ClassgraphIssue766.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java index ef3ee2be4..bc1ef0d8b 100644 --- a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java +++ b/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java @@ -27,8 +27,8 @@ public void testURLs() { } public static Set scan(final String annotation, final String urlStr) { - final ClassGraph classGraph = new ClassGraph() - .overrideClasspath(Set. of(urlStr)).disableNestedJarScanning().enableAnnotationInfo(); + final ClassGraph classGraph = new ClassGraph().overrideClasspath(urlStr).disableNestedJarScanning() + .enableAnnotationInfo(); try (ScanResult result = classGraph.scan()) { return result.getClassesWithAnnotation(annotation).getStandardClasses().getNames().stream() .collect(Collectors.toSet()); From ae2137d4e4fd9ee2f99d34bd39f959266fff642e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 00:53:00 -0600 Subject: [PATCH 515/713] [maven-release-plugin] prepare release classgraph-4.8.158 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 17224a655..aa04aba3f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.158-SNAPSHOT + 4.8.158 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.150 + classgraph-4.8.158 From 51a2fc3c8f7e92592e5032e7ed14e956ea9eccf9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 00:57:43 -0600 Subject: [PATCH 516/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aa04aba3f..66d836030 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.158 + 4.8.159-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From dbc40c872ad90f679d28194902096893269bdbdd Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 8 May 2023 01:48:04 -0700 Subject: [PATCH 517/713] Fix regression on Windows --- .../io/github/classgraph/classpath/ClasspathOrder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index bfe3f565f..72041b83c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -72,8 +72,8 @@ public class ClasspathOrder { /** Suffixes for automatic package roots, e.g. "!/BOOT-INF/classes". */ private static final List AUTOMATIC_PACKAGE_ROOT_SUFFIXES = new ArrayList<>(); - /** Match URL schemes. */ - private static final Pattern schemeMatcher = Pattern.compile("^[a-zA-Z+\\-.]+:"); + /** Match URL schemes (must consist of at least two chars, otherwise this is Windows drive letter). */ + private static final Pattern schemeMatcher = Pattern.compile("^[a-zA-Z][a-zA-Z+\\-.]+:"); static { for (final String prefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { @@ -338,6 +338,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Fall through } if (pathElementURL == null) { + // Escape percentage characters in URLs (#255) final String urlStr = pathElementStr.replace("%", "%25"); try { pathElementURL = new URL(urlStr); From f9f8ac8262caa908da98168b321e450ef79415d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 02:56:00 -0600 Subject: [PATCH 518/713] [maven-release-plugin] prepare release classgraph-4.8.159 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 66d836030..cfc878d3c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.159-SNAPSHOT + 4.8.159 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.158 + classgraph-4.8.159 From 754aba0a2ceaf506d755714e971d8a114f6d3a4f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 8 May 2023 02:56:03 -0600 Subject: [PATCH 519/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cfc878d3c..81b9ff068 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.159 + 4.8.160-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.159 + classgraph-4.8.158 From 7eed860c52b3d034b08336a5d5d8a2f10b83e41c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 02:56:29 +0000 Subject: [PATCH 520/713] Bump maven-gpg-plugin from 3.0.1 to 3.1.0 Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.0.1 to 3.1.0. - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.0.1...maven-gpg-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81b9ff068..78583996f 100644 --- a/pom.xml +++ b/pom.xml @@ -209,7 +209,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 org.sonatype.plugins From b9ece8884c03c9ec60a8315fef993ea55d4cbed0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 02:56:32 +0000 Subject: [PATCH 521/713] Bump animal-sniffer-enforcer-rule from 1.22 to 1.23 Bumps [animal-sniffer-enforcer-rule](https://github.com/mojohaus/animal-sniffer) from 1.22 to 1.23. - [Release notes](https://github.com/mojohaus/animal-sniffer/releases) - [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.22...1.23) --- updated-dependencies: - dependency-name: org.codehaus.mojo:animal-sniffer-enforcer-rule dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81b9ff068..2c3de4041 100644 --- a/pom.xml +++ b/pom.xml @@ -255,7 +255,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.22 + 1.23 From 02c75875f18f76624cdda916b229516838092e3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 02:56:35 +0000 Subject: [PATCH 522/713] Bump maven-resources-plugin from 3.3.0 to 3.3.1 Bumps [maven-resources-plugin](https://github.com/apache/maven-resources-plugin) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.3.0...maven-resources-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81b9ff068..6f16cf407 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins From e523453bedb331e726c01cfea23d3489d1bc7473 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 02:56:39 +0000 Subject: [PATCH 523/713] Bump junit-jupiter from 5.9.2 to 5.9.3 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.9.2 to 5.9.3. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.2...r5.9.3) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 81b9ff068..05bf39469 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ org.junit.jupiter junit-jupiter - 5.9.2 + 5.9.3 test From 46839873701ed289a36d07b6e0bbbaa7612ccc64 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 22 May 2023 17:27:46 +0200 Subject: [PATCH 524/713] fixed order of classes to determine inherited methods #772 --- .../java/io/github/classgraph/ClassInfo.java | 86 +++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 9099c697a..aff3faa01 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -174,6 +174,11 @@ public class ClassInfo extends ScanResultObject implements Comparable */ private transient List overrideOrder; + /** + * The override order for a class' methods (base class, followed by superclasses, followed by interfaces). + */ + private transient List methodOverrideOrder; + // ------------------------------------------------------------------------------------------------------------- /** The modifier bit for annotations. */ @@ -1553,7 +1558,7 @@ public boolean hasDeclaredMethod(final String methodName) { * @return true if this class or one of its superclasses or interfaces declares a method of the given name. */ public boolean hasMethod(final String methodName) { - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getMethodOverrideOrder()) { if (ci.hasDeclaredMethod(methodName)) { return true; } @@ -1611,7 +1616,7 @@ public boolean hasMethodAnnotation(final Class methodAnnot * annotation. */ public boolean hasMethodAnnotation(final String methodAnnotationName) { - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getMethodOverrideOrder()) { if (ci.hasDeclaredMethodAnnotation(methodAnnotationName)) { return true; } @@ -1668,7 +1673,7 @@ public boolean hasMethodParameterAnnotation(final Class me * @return true if this class or one of its superclasses or interfaces has a method with the named annotation. */ public boolean hasMethodParameterAnnotation(final String methodParameterAnnotationName) { - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getMethodOverrideOrder()) { if (ci.hasDeclaredMethodParameterAnnotation(methodParameterAnnotationName)) { return true; } @@ -1702,7 +1707,7 @@ private List getOverrideOrder(final Set visited, final Lis } /** - * Get the order that fields and methods are overridden in (base class first). + * Get the order that fields are overridden in (base class first). * * @return the override order */ @@ -1713,6 +1718,77 @@ private List getOverrideOrder() { return overrideOrder; } + + /** + * Recurse to collect classes and interfaces in the order of overridden methods, in descending priority. + *

+ * First collects all direct super classes, as their methods always have a higher priority than any method declared + * by an interface. + * Iterates over interfaces and inserts those extending already found interfaces before them in the output. + * The order of unrelated interfaces is unspecified. + *

+ * See Java Language Specification 8.4.8 for details. + * + * @param visited nonnull set of already visited ClassInfos + * @param overrideOrderOut nonnull outgoing list of ClassInfos in descending override order. + * @return the overrideOrderOut instance + */ + private List getMethodOverrideOrder(final Set visited, final List overrideOrderOut) { + if (!visited.add(this)) { + return overrideOrderOut; + } + //collect concrete super classes first, simply add to overrideOrder + if (!isInterfaceOrAnnotation()) { + overrideOrderOut.add(this); + //iterate over direct super classes first, they have the highest priority regarding method overrides + final ClassInfo superclass = getSuperclass(); + if (superclass != null) { + superclass.getMethodOverrideOrder(visited, overrideOrderOut); + } + for (final ClassInfo iface : getInterfaces()) { + iface.getMethodOverrideOrder(visited, overrideOrderOut); + } + return overrideOrderOut; + } + //overrideOrderOut already contains all concrete classes now + //this is an interface. If one of the extended interfaces is already in the output, then this needs to be + // added before it. + //Otherwise, this is unrelated to all collected ClassInfo so far and can simply be added to the result. + //The compiler should've prevented inheriting unrelated interfaces with methods having the same signature. + // Can still happen thanks to dynamically linking a different interface during runtime, for which the returned order is undefined. + ClassInfoList interfaces = getInterfaces(); + int minIndex = Integer.MAX_VALUE; + for (ClassInfo iface : interfaces) { + if (!visited.contains(iface)) { + continue; + } + int currIdx = overrideOrderOut.indexOf(iface); + minIndex = currIdx >= 0 && currIdx < minIndex ? currIdx : minIndex; + } + if (minIndex == Integer.MAX_VALUE) { + overrideOrderOut.add(this); + } else { + overrideOrderOut.add(minIndex, this); + } + for (final ClassInfo iface : interfaces) { + iface.getMethodOverrideOrder(visited, overrideOrderOut); + } + return overrideOrderOut; + } + + + /** + * Get the order that methods are overridden in. + * + * @return the override order + */ + private List getMethodOverrideOrder() { + if (methodOverrideOrder == null) { + methodOverrideOrder = getMethodOverrideOrder(new HashSet(), new ArrayList()); + } + return methodOverrideOrder; + } + // ------------------------------------------------------------------------------------------------------------- // Standard classes @@ -2229,7 +2305,7 @@ private MethodInfoList getMethodInfo(final String methodName, final boolean getN // Implement method/constructor overriding final MethodInfoList methodInfoList = new MethodInfoList(); final Set> nameAndTypeDescriptorSet = new HashSet<>(); - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getMethodOverrideOrder()) { for (final MethodInfo mi : ci.getDeclaredMethodInfo(methodName, getNormalMethods, getConstructorMethods, getStaticInitializerMethods)) { // If method/constructor has not been overridden by method of same name and type descriptor From 4a8f0eca16928cafdb5bd72a2d599229f636091a Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 23 May 2023 10:00:29 +0200 Subject: [PATCH 525/713] #772 unittests for method ordering --- .../classgraph/issues/issue772/ExampleA.java | 14 ++++ .../classgraph/issues/issue772/ExampleB.java | 9 +++ .../classgraph/issues/issue772/ExampleC.java | 11 +++ .../issue772/MethodOverrideOrderTest.java | 77 +++++++++++++++++++ .../issues/issue772/MyCloseable.java | 13 ++++ 5 files changed, 124 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue772/ExampleA.java create mode 100644 src/test/java/io/github/classgraph/issues/issue772/ExampleB.java create mode 100644 src/test/java/io/github/classgraph/issues/issue772/ExampleC.java create mode 100644 src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java create mode 100644 src/test/java/io/github/classgraph/issues/issue772/MyCloseable.java diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java new file mode 100644 index 000000000..eb7d66396 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java @@ -0,0 +1,14 @@ +package io.github.classgraph.issues.issue772; + +/** + * Test case A for selecting the 'Close' method of Child. + * Rather simple case of symmetrical extending classes. + */ +@SuppressWarnings("unused") +public abstract class ExampleA implements AutoCloseable { + + + public abstract static class Child extends ExampleA implements MyCloseable { + + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java new file mode 100644 index 000000000..d2e06e59a --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java @@ -0,0 +1,9 @@ +package io.github.classgraph.issues.issue772; + +@SuppressWarnings("unused") +public abstract class ExampleB implements MyCloseable { + + public abstract static class Child extends ExampleB implements AutoCloseable { + + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java new file mode 100644 index 000000000..60fcb18e9 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java @@ -0,0 +1,11 @@ +package io.github.classgraph.issues.issue772; + +@SuppressWarnings("unused") +public abstract class ExampleC implements AutoCloseable { + + public abstract void close(); + + public abstract static class Child extends ExampleC implements MyCloseable { + + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java new file mode 100644 index 000000000..455b69942 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java @@ -0,0 +1,77 @@ +package io.github.classgraph.issues.issue772; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfoList; +import io.github.classgraph.ScanResult; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests the order of classes overriding one another when selecting methods + */ +public class MethodOverrideOrderTest { + + private static ScanResult scanResult; + + @BeforeAll + public static void setup() { + scanResult = new ClassGraph() + .acceptPackages(MethodOverrideOrderTest.class.getPackage().getName()) + .enableMethodInfo() + .scan(); + } + + @AfterAll + public static void teardown() { + scanResult.close(); + scanResult = null; + } + + + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingA() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleA$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + //Reflection in JDK8 will source the method AutoCloseable as well, works as expected from at least JDK11+ + // ClassLoader.getSystemClassLoader().loadClass("io.github.classgraph.issues.issue772.ExampleA$Child").getMethod("close") + } + + + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingB() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleB$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + } + + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingC() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleC$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.ExampleC"); + } + +} diff --git a/src/test/java/io/github/classgraph/issues/issue772/MyCloseable.java b/src/test/java/io/github/classgraph/issues/issue772/MyCloseable.java new file mode 100644 index 000000000..574fe96f8 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue772/MyCloseable.java @@ -0,0 +1,13 @@ +package io.github.classgraph.issues.issue772; + +import java.io.Closeable; + +/** + * An interface overriding another, its methods should always be 'preferred' before methods from Closeable + */ +public interface MyCloseable extends Closeable { + + //override close without the exception, good candidate for a default method in JDK8+ + @Override + void close(); +} From 09933789397f89de40e6b28937654043d3a38a0d Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 23 May 2023 10:01:54 +0200 Subject: [PATCH 526/713] use project indentation of 4 characters --- .../classgraph/issues/issue772/ExampleA.java | 4 +- .../classgraph/issues/issue772/ExampleB.java | 4 +- .../classgraph/issues/issue772/ExampleC.java | 6 +- .../issue772/MethodOverrideOrderTest.java | 102 +++++++++--------- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java index eb7d66396..60a9279b9 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java @@ -8,7 +8,7 @@ public abstract class ExampleA implements AutoCloseable { - public abstract static class Child extends ExampleA implements MyCloseable { + public abstract static class Child extends ExampleA implements MyCloseable { - } + } } diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java index d2e06e59a..461e394af 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleB.java @@ -3,7 +3,7 @@ @SuppressWarnings("unused") public abstract class ExampleB implements MyCloseable { - public abstract static class Child extends ExampleB implements AutoCloseable { + public abstract static class Child extends ExampleB implements AutoCloseable { - } + } } diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java index 60fcb18e9..0eed9b13a 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleC.java @@ -3,9 +3,9 @@ @SuppressWarnings("unused") public abstract class ExampleC implements AutoCloseable { - public abstract void close(); + public abstract void close(); - public abstract static class Child extends ExampleC implements MyCloseable { + public abstract static class Child extends ExampleC implements MyCloseable { - } + } } diff --git a/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java index 455b69942..20379e4a8 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java +++ b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java @@ -15,63 +15,63 @@ */ public class MethodOverrideOrderTest { - private static ScanResult scanResult; + private static ScanResult scanResult; - @BeforeAll - public static void setup() { - scanResult = new ClassGraph() - .acceptPackages(MethodOverrideOrderTest.class.getPackage().getName()) - .enableMethodInfo() - .scan(); - } + @BeforeAll + public static void setup() { + scanResult = new ClassGraph() + .acceptPackages(MethodOverrideOrderTest.class.getPackage().getName()) + .enableMethodInfo() + .scan(); + } - @AfterAll - public static void teardown() { - scanResult.close(); - scanResult = null; - } + @AfterAll + public static void teardown() { + scanResult.close(); + scanResult = null; + } - /** - * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. - * Case of the child class implementing the inherited interface. - */ - @Test - public void interfaceMethodOrderingA() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleA$Child"); - assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); - assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); - //Reflection in JDK8 will source the method AutoCloseable as well, works as expected from at least JDK11+ - // ClassLoader.getSystemClassLoader().loadClass("io.github.classgraph.issues.issue772.ExampleA$Child").getMethod("close") - } + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingA() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleA$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + //Reflection in JDK8 will source the method AutoCloseable as well, works as expected from at least JDK11+ + // ClassLoader.getSystemClassLoader().loadClass("io.github.classgraph.issues.issue772.ExampleA$Child").getMethod("close") + } - /** - * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. - * Case of the child class implementing the inherited interface. - */ - @Test - public void interfaceMethodOrderingB() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleB$Child"); - assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); - assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); - } + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingB() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleB$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + } - /** - * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. - * Case of the child class implementing the inherited interface. - */ - @Test - public void interfaceMethodOrderingC() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleC$Child"); - assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); - assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.ExampleC"); - } + /** + * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. + * Case of the child class implementing the inherited interface. + */ + @Test + public void interfaceMethodOrderingC() { + ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleC$Child"); + assertThat(classInfo).isNotNull(); + MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + assertThat(closeMethods.size()).isEqualTo(1); + assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.ExampleC"); + } } From 616f38d08d7f9d9c5d60379ac9154cde62a1d9fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 May 2023 02:32:59 -0600 Subject: [PATCH 527/713] Source > Cleanup --- .../java/io/github/classgraph/ClassInfo.java | 24 +++++------ .../classgraph/classpath/ClasspathOrder.java | 2 +- .../classgraph/issues/issue772/ExampleA.java | 4 +- .../issue772/MethodOverrideOrderTest.java | 40 +++++++++---------- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index aff3faa01..7e43e5abb 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1718,22 +1718,23 @@ private List getOverrideOrder() { return overrideOrder; } - /** * Recurse to collect classes and interfaces in the order of overridden methods, in descending priority. *

- * First collects all direct super classes, as their methods always have a higher priority than any method declared - * by an interface. - * Iterates over interfaces and inserts those extending already found interfaces before them in the output. - * The order of unrelated interfaces is unspecified. + * First collects all direct super classes, as their methods always have a higher priority than any method + * declared by an interface. Iterates over interfaces and inserts those extending already found interfaces + * before them in the output. The order of unrelated interfaces is unspecified. *

* See Java Language Specification 8.4.8 for details. * - * @param visited nonnull set of already visited ClassInfos - * @param overrideOrderOut nonnull outgoing list of ClassInfos in descending override order. + * @param visited + * nonnull set of already visited ClassInfos + * @param overrideOrderOut + * nonnull outgoing list of ClassInfos in descending override order. * @return the overrideOrderOut instance */ - private List getMethodOverrideOrder(final Set visited, final List overrideOrderOut) { + private List getMethodOverrideOrder(final Set visited, + final List overrideOrderOut) { if (!visited.add(this)) { return overrideOrderOut; } @@ -1756,13 +1757,13 @@ private List getMethodOverrideOrder(final Set visited, fin //Otherwise, this is unrelated to all collected ClassInfo so far and can simply be added to the result. //The compiler should've prevented inheriting unrelated interfaces with methods having the same signature. // Can still happen thanks to dynamically linking a different interface during runtime, for which the returned order is undefined. - ClassInfoList interfaces = getInterfaces(); + final ClassInfoList interfaces = getInterfaces(); int minIndex = Integer.MAX_VALUE; - for (ClassInfo iface : interfaces) { + for (final ClassInfo iface : interfaces) { if (!visited.contains(iface)) { continue; } - int currIdx = overrideOrderOut.indexOf(iface); + final int currIdx = overrideOrderOut.indexOf(iface); minIndex = currIdx >= 0 && currIdx < minIndex ? currIdx : minIndex; } if (minIndex == Integer.MAX_VALUE) { @@ -1776,7 +1777,6 @@ private List getMethodOverrideOrder(final Set visited, fin return overrideOrderOut; } - /** * Get the order that methods are overridden in. * diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java index 72041b83c..7b7c354e5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -338,7 +338,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Fall through } if (pathElementURL == null) { - // Escape percentage characters in URLs (#255) + // Escape percentage characters in URLs (#255) final String urlStr = pathElementStr.replace("%", "%25"); try { pathElementURL = new URL(urlStr); diff --git a/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java index 60a9279b9..2c6917afd 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java +++ b/src/test/java/io/github/classgraph/issues/issue772/ExampleA.java @@ -1,13 +1,11 @@ package io.github.classgraph.issues.issue772; /** - * Test case A for selecting the 'Close' method of Child. - * Rather simple case of symmetrical extending classes. + * Test case A for selecting the 'Close' method of Child. Rather simple case of symmetrical extending classes. */ @SuppressWarnings("unused") public abstract class ExampleA implements AutoCloseable { - public abstract static class Child extends ExampleA implements MyCloseable { } diff --git a/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java index 20379e4a8..9ff864286 100644 --- a/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java +++ b/src/test/java/io/github/classgraph/issues/issue772/MethodOverrideOrderTest.java @@ -1,14 +1,15 @@ package io.github.classgraph.issues.issue772; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.MethodInfoList; -import io.github.classgraph.ScanResult; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfoList; +import io.github.classgraph.ScanResult; /** * Tests the order of classes overriding one another when selecting methods @@ -19,10 +20,8 @@ public class MethodOverrideOrderTest { @BeforeAll public static void setup() { - scanResult = new ClassGraph() - .acceptPackages(MethodOverrideOrderTest.class.getPackage().getName()) - .enableMethodInfo() - .scan(); + scanResult = new ClassGraph().acceptPackages(MethodOverrideOrderTest.class.getPackage().getName()) + .enableMethodInfo().scan(); } @AfterAll @@ -31,34 +30,34 @@ public static void teardown() { scanResult = null; } - /** * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. * Case of the child class implementing the inherited interface. */ @Test public void interfaceMethodOrderingA() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleA$Child"); + final ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleA$Child"); assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + final MethodInfoList closeMethods = classInfo.getMethodInfo("close"); assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + assertThat(closeMethods.get(0).getClassInfo().getName()) + .isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); //Reflection in JDK8 will source the method AutoCloseable as well, works as expected from at least JDK11+ // ClassLoader.getSystemClassLoader().loadClass("io.github.classgraph.issues.issue772.ExampleA$Child").getMethod("close") } - /** * Tests if the correct method is selected if a class implements from two interfaces that inherit from another. * Case of the child class implementing the inherited interface. */ @Test public void interfaceMethodOrderingB() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleB$Child"); + final ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleB$Child"); assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + final MethodInfoList closeMethods = classInfo.getMethodInfo("close"); assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); + assertThat(closeMethods.get(0).getClassInfo().getName()) + .isEqualTo("io.github.classgraph.issues.issue772.MyCloseable"); } /** @@ -67,11 +66,12 @@ public void interfaceMethodOrderingB() { */ @Test public void interfaceMethodOrderingC() { - ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleC$Child"); + final ClassInfo classInfo = scanResult.getClassInfo("io.github.classgraph.issues.issue772.ExampleC$Child"); assertThat(classInfo).isNotNull(); - MethodInfoList closeMethods = classInfo.getMethodInfo("close"); + final MethodInfoList closeMethods = classInfo.getMethodInfo("close"); assertThat(closeMethods.size()).isEqualTo(1); - assertThat(closeMethods.get(0).getClassInfo().getName()).isEqualTo("io.github.classgraph.issues.issue772.ExampleC"); + assertThat(closeMethods.get(0).getClassInfo().getName()) + .isEqualTo("io.github.classgraph.issues.issue772.ExampleC"); } } From b39981a6d540ef4007fa37e2d7ca17e4e9bb1fd3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 May 2023 02:35:25 -0600 Subject: [PATCH 528/713] Update private method name and comments --- .../java/io/github/classgraph/ClassInfo.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 7e43e5abb..02221f4ee 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1475,7 +1475,7 @@ public boolean hasDeclaredField(final String fieldName) { * @return true if this class or one of its superclasses declares a field of the given name. */ public boolean hasField(final String fieldName) { - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getFieldOverrideOrder()) { if (ci.hasDeclaredField(fieldName)) { return true; } @@ -1531,7 +1531,7 @@ public boolean hasFieldAnnotation(final Class fieldAnnotat * @return true if this class or one of its superclasses declares a field with the named annotation. */ public boolean hasFieldAnnotation(final String fieldAnnotationName) { - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getFieldOverrideOrder()) { if (ci.hasDeclaredFieldAnnotation(fieldAnnotationName)) { return true; } @@ -1684,7 +1684,7 @@ public boolean hasMethodParameterAnnotation(final String methodParameterAnnotati // ------------------------------------------------------------------------------------------------------------- /** - * Recurse to interfaces and superclasses to get the order that fields and methods are overridden in. + * Recurse to interfaces and superclasses to get the order that fields are overridden in. * * @param visited * visited @@ -1692,15 +1692,16 @@ public boolean hasMethodParameterAnnotation(final String methodParameterAnnotati * the override order * @return the override order */ - private List getOverrideOrder(final Set visited, final List overrideOrderOut) { + private List getFieldOverrideOrder(final Set visited, + final List overrideOrderOut) { if (visited.add(this)) { overrideOrderOut.add(this); for (final ClassInfo iface : getInterfaces()) { - iface.getOverrideOrder(visited, overrideOrderOut); + iface.getFieldOverrideOrder(visited, overrideOrderOut); } final ClassInfo superclass = getSuperclass(); if (superclass != null) { - superclass.getOverrideOrder(visited, overrideOrderOut); + superclass.getFieldOverrideOrder(visited, overrideOrderOut); } } return overrideOrderOut; @@ -1711,9 +1712,9 @@ private List getOverrideOrder(final Set visited, final Lis * * @return the override order */ - private List getOverrideOrder() { + private List getFieldOverrideOrder() { if (overrideOrder == null) { - overrideOrder = getOverrideOrder(new HashSet(), new ArrayList()); + overrideOrder = getFieldOverrideOrder(new HashSet(), new ArrayList()); } return overrideOrder; } @@ -1728,9 +1729,9 @@ private List getOverrideOrder() { * See Java Language Specification 8.4.8 for details. * * @param visited - * nonnull set of already visited ClassInfos + * non-null set of already visited ClassInfos * @param overrideOrderOut - * nonnull outgoing list of ClassInfos in descending override order. + * non-null outgoing list of ClassInfos in descending override order. * @return the overrideOrderOut instance */ private List getMethodOverrideOrder(final Set visited, @@ -1751,12 +1752,13 @@ private List getMethodOverrideOrder(final Set visited, } return overrideOrderOut; } - //overrideOrderOut already contains all concrete classes now - //this is an interface. If one of the extended interfaces is already in the output, then this needs to be + // overrideOrderOut already contains all concrete classes now. + // This is an interface. If one of the extended interfaces is already in the output, then this needs to be // added before it. - //Otherwise, this is unrelated to all collected ClassInfo so far and can simply be added to the result. - //The compiler should've prevented inheriting unrelated interfaces with methods having the same signature. - // Can still happen thanks to dynamically linking a different interface during runtime, for which the returned order is undefined. + // Otherwise, this is unrelated to all collected ClassInfo so far and can simply be added to the result. + // The compiler should've prevented inheriting unrelated interfaces with methods having the same signature. + // Can still happen thanks to dynamically linking a different interface during runtime, for which the + // returned order is undefined. final ClassInfoList interfaces = getInterfaces(); int minIndex = Integer.MAX_VALUE; for (final ClassInfo iface : interfaces) { @@ -1771,6 +1773,7 @@ private List getMethodOverrideOrder(final Set visited, } else { overrideOrderOut.add(minIndex, this); } + // Add interfaces to end of override order for (final ClassInfo iface : interfaces) { iface.getMethodOverrideOrder(visited, overrideOrderOut); } @@ -2758,7 +2761,7 @@ public FieldInfoList getFieldInfo() { // Implement field overriding final FieldInfoList fieldInfoList = new FieldInfoList(); final Set fieldNameSet = new HashSet<>(); - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getFieldOverrideOrder()) { for (final FieldInfo fi : ci.getDeclaredFieldInfo()) { // If field has not been overridden by field of same name if (fieldNameSet.add(fi.getName())) { @@ -2874,7 +2877,7 @@ public FieldInfo getFieldInfo(final String fieldName) { throw new IllegalArgumentException("Please call ClassGraph#enableFieldInfo() before #scan()"); } // Implement field overriding - for (final ClassInfo ci : getOverrideOrder()) { + for (final ClassInfo ci : getFieldOverrideOrder()) { final FieldInfo fi = ci.getDeclaredFieldInfo(fieldName); if (fi != null) { return fi; From 9ff32e6459e845683e180d290320d1b87bb44acd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 May 2023 02:38:22 -0600 Subject: [PATCH 529/713] Bump maven-compiler-plugin version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc57706c0..1dea9133d 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.10.1 org.apache.maven.plugins From ab487be4d213294859d963ec3dfdb976c4ce49c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 May 2023 02:48:03 -0600 Subject: [PATCH 530/713] [maven-release-plugin] prepare release classgraph-4.8.160 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1dea9133d..ed08516d0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.160-SNAPSHOT + 4.8.160 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.158 + classgraph-4.8.160 From a8641d59b6c3a5999472d8e5d7fcc4ec34e116ab Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 May 2023 02:48:05 -0600 Subject: [PATCH 531/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ed08516d0..26bcbb071 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.160 + 4.8.161-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.160 + classgraph-4.8.158 From 27840ea775de96f95cb6a375d7c31a083e662da5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 02:56:37 +0000 Subject: [PATCH 532/713] Bump maven-source-plugin from 3.2.1 to 3.3.0 Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26bcbb071..7333803b8 100644 --- a/pom.xml +++ b/pom.xml @@ -199,7 +199,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 org.apache.maven.plugins From 9951e3d7a472287bb40fa20861a803073b801760 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 02:56:42 +0000 Subject: [PATCH 533/713] Bump maven-enforcer-plugin from 3.2.1 to 3.3.0 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.2.1...enforcer-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26bcbb071..83ec7040c 100644 --- a/pom.xml +++ b/pom.xml @@ -164,7 +164,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.2.1 + 3.3.0 org.apache.maven.plugins From 39c3f439fd99a0b78014932170aad5cf807f220d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 02:56:46 +0000 Subject: [PATCH 534/713] Bump maven-compiler-plugin from 3.10.1 to 3.11.0 Bumps [maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.10.1 to 3.11.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.10.1...maven-compiler-plugin-3.11.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26bcbb071..b63dab948 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 org.apache.maven.plugins From 6af5a14f263a81ca709500540bdcc70e0547ef56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 02:56:50 +0000 Subject: [PATCH 535/713] Bump maven-install-plugin from 3.1.0 to 3.1.1 Bumps [maven-install-plugin](https://github.com/apache/maven-install-plugin) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/apache/maven-install-plugin/releases) - [Commits](https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.1.0...maven-install-plugin-3.1.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-install-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26bcbb071..54698cb16 100644 --- a/pom.xml +++ b/pom.xml @@ -236,7 +236,7 @@ org.apache.maven.plugins maven-install-plugin - 3.1.0 + 3.1.1 org.apache.maven.plugins From 60a3f46a25c393bde5cc51b13709135e33f7c34b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 02:56:49 +0000 Subject: [PATCH 536/713] Bump maven-release-plugin from 3.0.0-M7 to 3.0.1 Bumps [maven-release-plugin](https://github.com/apache/maven-release) from 3.0.0-M7 to 3.0.1. - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0-M7...maven-release-3.0.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26bcbb071..b8fb735b0 100644 --- a/pom.xml +++ b/pom.xml @@ -219,7 +219,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M7 + 3.0.1 From fa1891de9d39235bd2aad2549555ffb6630be5ae Mon Sep 17 00:00:00 2001 From: Gabor Garancsi Date: Wed, 5 Jul 2023 16:13:24 +0200 Subject: [PATCH 537/713] Never return null in getResourcesWithPath() #780 Since cd213ef getResourcesWithPath() returned null if it's called n-th times for a non-scanned or non-existing path. --- .../java/io/github/classgraph/ScanResult.java | 6 +++--- .../issues/issue780/Issue780Test.java | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e69e19b38..3b040e158 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -516,12 +516,12 @@ public ResourceList getResourcesWithPath(final String resourcePath) { } final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true, /* removeFinalSlash = */ true); + ResourceList matchingResources = null; if (getResourcesWithPathCallCount.incrementAndGet() > 3) { // If numerous calls are made, produce and cache a single HashMap for O(1) access time - return getAllResourcesAsMap().get(path); + matchingResources = getAllResourcesAsMap().get(path); } else { // If just a few calls are made, directly search for resource with the requested path - ResourceList matchingResources = null; for (final ClasspathElement classpathElt : classpathOrder) { for (final Resource res : classpathElt.acceptedResources) { if (res.getPath().equals(path)) { @@ -532,8 +532,8 @@ public ResourceList getResourcesWithPath(final String resourcePath) { } } } - return matchingResources == null ? ResourceList.EMPTY_LIST : matchingResources; } + return matchingResources == null ? ResourceList.EMPTY_LIST : matchingResources; } /** diff --git a/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java b/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java new file mode 100644 index 000000000..f557fe6da --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java @@ -0,0 +1,21 @@ +package io.github.classgraph.issues.issue780; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Issue780Test { + /** + * Issue 780. + */ + @Test + public void getResourcesWithPathShouldNeverReturnNull() { + try (ScanResult result = new ClassGraph().scan()) { + for (int i = 0; i < 10; i++) { + assertThat(result.getResourcesWithPath("/some/non/existing/path")).isNotNull().isEmpty(); + } + } + } +} From acc366121750733d98d39fffb7f02b9756c80ef0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Jul 2023 16:18:53 -0600 Subject: [PATCH 538/713] Fix new lint warnings --- .classpath | 7 ++ pom.xml | 6 +- .../io/github/classgraph/AnnotationInfo.java | 2 +- .../classgraph/ClassGraphClassLoader.java | 4 +- .../classgraph/ClasspathElementDir.java | 2 +- .../classgraph/ClasspathElementModule.java | 12 +- .../classgraph/ClasspathElementZip.java | 2 +- .../io/github/classgraph/ResourceList.java | 24 ++-- .../classgraph/classpath/CallStackReader.java | 2 +- .../fastzipfilereader/NestedJarHandler.java | 103 ++++++++++-------- .../reflection/ReflectionUtils.java | 2 +- .../reflection/StandardReflectionDriver.java | 2 +- 12 files changed, 88 insertions(+), 80 deletions(-) diff --git a/.classpath b/.classpath index 5576b4361..1234d88f1 100644 --- a/.classpath +++ b/.classpath @@ -22,6 +22,7 @@ + @@ -36,5 +37,11 @@ + + + + + + diff --git a/pom.xml b/pom.xml index a2fa3bade..745989685 100644 --- a/pom.xml +++ b/pom.xml @@ -413,8 +413,10 @@ 8 false - -Xlint:all - -Werror + -Xlint:all + -Xlint:-options + -Werror + -Xbootclasspath:${env.JAVA_HOME}/jre/lib/rt.jar diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index cbf6425ee..356f1a41a 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -288,7 +288,7 @@ public ClassInfo getClassInfo() { public Annotation loadClassAndInstantiate() { final Class annotationClass = getClassInfo().loadClass(Annotation.class); return (Annotation) Proxy.newProxyInstance(annotationClass.getClassLoader(), - new Class[] { annotationClass }, new AnnotationInvocationHandler(annotationClass, this)); + new Class[] { annotationClass }, new AnnotationInvocationHandler(annotationClass, this)); } /** {@link InvocationHandler} for dynamically instantiating an {@link Annotation} object. */ diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 895d133b1..274b8383b 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -32,7 +32,6 @@ import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; -import java.nio.ByteBuffer; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collections; @@ -265,11 +264,10 @@ protected Class findClass(final String className) // Iterate through resources (only loading of first resource in the list will be attempted) // Load the content of the resource, and define a class from it try (Resource resourceToClose = resource) { - final ByteBuffer resourceByteBuffer = resource.read(); // TODO: is there any need to try java.lang.invoke.MethodHandles.Lookup.defineClass // via reflection (it's implemented in JDK 9), if the following fails? // See: https://bugs.openjdk.java.net/browse/JDK-8202999 - return defineClass(className, resourceByteBuffer, (ProtectionDomain) null); + return defineClass(className, resourceToClose.read(), (ProtectionDomain) null); } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); } catch (final LinkageError e) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 9a9c6a3e8..dd14e4324 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -264,7 +264,7 @@ public byte[] load() throws IOException { try (Resource res = this) { // Close this after use pathSlice = new PathSlice(resourcePath, nestedJarHandler); final byte[] bytes = pathSlice.load(); - length = bytes.length; + res.length = bytes.length; return bytes; } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 5ab2b4f49..83d54b2b5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -225,14 +225,14 @@ public byte[] load() throws IOException { try (Resource res = this) { // Close this after use read(); // Fill byteBuffer final byte[] byteArray; - if (byteBuffer.hasArray() && byteBuffer.position() == 0 - && byteBuffer.limit() == byteBuffer.capacity()) { - byteArray = byteBuffer.array(); + if (res.byteBuffer.hasArray() && res.byteBuffer.position() == 0 + && res.byteBuffer.limit() == res.byteBuffer.capacity()) { + byteArray = res.byteBuffer.array(); } else { - byteArray = new byte[byteBuffer.remaining()]; - byteBuffer.get(byteArray); + byteArray = new byte[res.byteBuffer.remaining()]; + res.byteBuffer.get(byteArray); } - length = byteArray.length; + res.length = byteArray.length; return byteArray; } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index c45b9b0ec..919144081 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -429,7 +429,7 @@ public byte[] load() throws IOException { } try (Resource res = this) { // Close this after use final byte[] byteArray = zipEntry.getSlice().load(); - length = byteArray.length; + res.length = byteArray.length; return byteArray; } } diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 4bf70a964..32b3e348c 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -364,8 +364,7 @@ public interface ByteArrayConsumerThrowsIOException { public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - final byte[] resourceContent = resource.load(); - byteArrayConsumer.accept(resource, resourceContent); + byteArrayConsumer.accept(resourceToClose, resourceToClose.load()); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); @@ -402,8 +401,7 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { public void forEachByteArrayIgnoringIOException(final ByteArrayConsumer byteArrayConsumer) { for (final Resource resource : this) { try (Resource resourceToClose = resource) { - final byte[] resourceContent = resource.load(); - byteArrayConsumer.accept(resource, resourceContent); + byteArrayConsumer.accept(resourceToClose, resourceToClose.load()); } catch (final IOException e) { // Ignore } @@ -424,8 +422,7 @@ public void forEachByteArrayThrowingIOException( final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try (Resource resourceToClose = resource) { - final byte[] resourceContent = resource.load(); - byteArrayConsumerThrowsIOException.accept(resource, resourceContent); + byteArrayConsumerThrowsIOException.accept(resourceToClose, resourceToClose.load()); } } } @@ -487,7 +484,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - inputStreamConsumer.accept(resource, resource.open()); + inputStreamConsumer.accept(resourceToClose, resourceToClose.open()); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); @@ -524,7 +521,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { public void forEachInputStreamIgnoringIOException(final InputStreamConsumer inputStreamConsumer) { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - inputStreamConsumer.accept(resource, resource.open()); + inputStreamConsumer.accept(resourceToClose, resourceToClose.open()); } catch (final IOException e) { // Ignore } @@ -546,7 +543,7 @@ public void forEachInputStreamThrowingIOException( final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - inputStreamConsumerThrowsIOException.accept(resource, resource.open()); + inputStreamConsumerThrowsIOException.accept(resourceToClose, resourceToClose.open()); } } } @@ -606,8 +603,7 @@ public interface ByteBufferConsumerThrowsIOException { public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - final ByteBuffer byteBuffer = resource.read(); - byteBufferConsumer.accept(resource, byteBuffer); + byteBufferConsumer.accept(resourceToClose, resourceToClose.read()); } catch (final IOException e) { if (!ignoreIOExceptions) { throw new IllegalArgumentException("Could not load resource " + resource, e); @@ -644,8 +640,7 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { public void forEachByteBufferIgnoringIOException(final ByteBufferConsumer byteBufferConsumer) { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - final ByteBuffer byteBuffer = resource.read(); - byteBufferConsumer.accept(resource, byteBuffer); + byteBufferConsumer.accept(resourceToClose, resourceToClose.read()); } catch (final IOException e) { // Ignore } @@ -666,8 +661,7 @@ public void forEachByteBufferThrowingIOException( final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try (final Resource resourceToClose = resource) { - final ByteBuffer byteBuffer = resource.read(); - byteBufferConsumerThrowsIOException.accept(resource, byteBuffer); + byteBufferConsumerThrowsIOException.accept(resourceToClose, resourceToClose.read()); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java index bbeb278f1..499996fa7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -76,7 +76,7 @@ private static Class[] getCallStackViaStackWalker() { .getMethod("getDeclaringClass"); stackWalkerClass.getMethod("forEach", consumerClass).invoke(stackWalkerInstance, // // InvocationHandler proxy for Consumer - Proxy.newProxyInstance(consumerClass.getClassLoader(), new Class[] { consumerClass }, + Proxy.newProxyInstance(consumerClass.getClassLoader(), new Class[] { consumerClass }, new InvocationHandler() { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 302a1dc20..d31cf8354 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -29,7 +29,6 @@ package nonapi.io.github.classgraph.fastzipfilereader; import java.io.BufferedOutputStream; -import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -548,61 +547,69 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo // Not a custom filesystem } } - - final URLConnection conn = url.openConnection(); - long contentLengthHint = -1L; - if (conn instanceof HttpURLConnection) { - // Get content length from HTTP headers, if available - final HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); - try (Closeable httpConnCloser = new Closeable() { - @Override - public void close() { - if (httpConn != null) { - httpConn.disconnect(); - } + try (final CloseableUrlConnection urlConn = new CloseableUrlConnection(url)) { + long contentLengthHint = -1L; + urlConn.conn.setConnectTimeout(HTTP_TIMEOUT); + urlConn.conn.connect(); + if (urlConn.httpConn != null) { + // Get content length from HTTP headers, if available + if (urlConn.httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new IOException( + "Got response code " + urlConn.httpConn.getResponseCode() + " for URL " + url); } - }) { - httpConn.setRequestMethod("GET"); - httpConn.setConnectTimeout(HTTP_TIMEOUT); - if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) { - contentLengthHint = httpConn.getContentLengthLong(); - if (contentLengthHint < -1L) { - contentLengthHint = -1L; - } - } else { - throw new IOException("Got response code " + httpConn.getResponseCode() + " for URL " + url); + } else if (url.getProtocol().equalsIgnoreCase("file")) { + // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that + // rewrites its URLs into "file:" URLs (see Issue400.java). + try { + // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile + // (this avoids going through an InputStream). Throws IOException if the file cannot be read. + final File file = Paths.get(url.toURI()).toFile(); + return new PhysicalZipFile(file, this, log); + + } catch (final Exception e) { + // Fall through -- unknown URL type } } - } else if (url.getProtocol().equalsIgnoreCase("file")) { - // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that - // rewrites its URLs into "file:" URLs (see Issue400.java). - try { - // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile - // (this avoids going through an InputStream). Throws IOException if the file cannot be read. - final File file = Paths.get(url.toURI()).toFile(); - return new PhysicalZipFile(file, this, log); + // Try to read content length hint + contentLengthHint = urlConn.conn.getContentLengthLong(); + if (contentLengthHint < -1L) { + contentLengthHint = -1L; + } + // Fetch content from URL + final LogNode subLog = log == null ? null : log.log("Downloading jar from URL " + jarURL); + try (InputStream inputStream = urlConn.conn.getInputStream()) { + // Fetch the jar contents from the URL's InputStream. If it doesn't fit in RAM, + // spill over to disk. + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(inputStream, contentLengthHint, jarURL, + this, subLog); + if (subLog != null) { + subLog.addElapsedTime(); + subLog.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " + + "the URL must be opened (possibly after an http(s) fetch) for every scan, " + + "and the same URL must also be separately opened by the ClassLoader *****"); + } + return physicalZipFile; - } catch (final Exception e) { - // Fall through to open URL as InputStream below + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + jarURL); } } + } - // Fetch content from URL - final LogNode subLog = log == null ? null : log.log("Downloading jar from URL " + jarURL); - try (InputStream inputStream = conn.getInputStream()) { - // Fetch the jar contents from the URL's InputStream. If it doesn't fit in RAM, spill over to disk. - final PhysicalZipFile physicalZipFile = new PhysicalZipFile(inputStream, contentLengthHint, jarURL, - this, subLog); - if (subLog != null) { - subLog.addElapsedTime(); - subLog.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " - + "the URL must be opened (possibly after an http(s) fetch) for every scan, " - + "and the same URL must also be separately opened by the ClassLoader *****"); - } - return physicalZipFile; + private static class CloseableUrlConnection implements AutoCloseable { + public final URLConnection conn; + public final HttpURLConnection httpConn; + + public CloseableUrlConnection(final URL url) throws IOException { + conn = url.openConnection(); + httpConn = conn instanceof HttpURLConnection ? (HttpURLConnection) conn : null; + } - } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + jarURL); + @Override + public void close() { + if (httpConn != null) { + httpConn.disconnect(); + } } } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 81a7702d4..14815a0e0 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -418,7 +418,7 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg public T doPrivileged(final Callable callable) throws Throwable { if (accessControllerDoPrivileged != null) { final Object privilegedAction = Proxy.newProxyInstance(privilegedActionClass.getClassLoader(), - new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); + new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); return (T) accessControllerDoPrivileged.invoke(null, privilegedAction); } else { // Fall back to invoking in a non-privileged context diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java index 6e0580d0b..410398700 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/StandardReflectionDriver.java @@ -93,7 +93,7 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg private T doPrivileged(final Callable callable) throws Throwable { if (accessControllerDoPrivileged != null) { final Object privilegedAction = Proxy.newProxyInstance(privilegedActionClass.getClassLoader(), - new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); + new Class[] { privilegedActionClass }, new PrivilegedActionInvocationHandler(callable)); return (T) accessControllerDoPrivileged.invoke(null, privilegedAction); } else { // Fall back to invoking in a non-privileged context From 7b4d611e528008939eb6cff3571cf8ef4e646513 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Jul 2023 16:21:42 -0600 Subject: [PATCH 539/713] [maven-release-plugin] prepare release classgraph-4.8.161 --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 745989685..59cad4c37 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.161-SNAPSHOT + 4.8.161 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.158 + classgraph-4.8.161 @@ -416,7 +416,6 @@ -Xlint:all -Xlint:-options -Werror - -Xbootclasspath:${env.JAVA_HOME}/jre/lib/rt.jar From 9be432eecfd836c5772220947ad544691c3d4cfe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Jul 2023 16:21:45 -0600 Subject: [PATCH 540/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59cad4c37..b5b67e42f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.161 + 4.8.162-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From dd19b749ff80e79ee813c70504a6282699d0abe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 02:43:26 +0000 Subject: [PATCH 541/713] Bump maven-clean-plugin from 3.2.0 to 3.3.1 Bumps [maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.2.0 to 3.3.1. - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.2.0...maven-clean-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..fcdbbae23 100644 --- a/pom.xml +++ b/pom.xml @@ -226,7 +226,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.2.0 + 3.3.1 org.apache.maven.plugins From d21a9043d6499fec6db0e8ab23bdc119399b57e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 02:43:29 +0000 Subject: [PATCH 542/713] Bump maven-surefire-plugin from 3.1.0 to 3.1.2 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.0...surefire-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..1545bdacc 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.0 + 3.1.2 org.codehaus.mojo From e857e47592d8a40e5236358cd945dc5d964eb42b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 02:43:33 +0000 Subject: [PATCH 543/713] Bump maven-deploy-plugin from 3.1.0 to 3.1.1 Bumps [maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.0...maven-deploy-plugin-3.1.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..4424c28b7 100644 --- a/pom.xml +++ b/pom.xml @@ -231,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.0 + 3.1.1 org.apache.maven.plugins From 843f236ecf94c684a4fcc69d77235e046d5573b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 02:43:37 +0000 Subject: [PATCH 544/713] Bump build-helper-maven-plugin from 3.3.0 to 3.4.0 Bumps [build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/build-helper-maven-plugin-3.3.0...3.4.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..eeccae45a 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.4.0 org.apache.maven.plugins From 2be3c62bfc97ce466200697f571f1b23144cb767 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 02:43:41 +0000 Subject: [PATCH 545/713] Bump jimfs from 1.2 to 1.3.0 Bumps [jimfs](https://github.com/google/jimfs) from 1.2 to 1.3.0. - [Release notes](https://github.com/google/jimfs/releases) - [Commits](https://github.com/google/jimfs/compare/v1.2...v1.3.0) --- updated-dependencies: - dependency-name: com.google.jimfs:jimfs dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..49fc93576 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ com.google.jimfs jimfs - 1.2 + 1.3.0 test From 217ef1cdcdd799baef0d42cb88829a0367184874 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Jul 2023 15:49:44 -0600 Subject: [PATCH 546/713] Move to newer Nexus server, s01.oss.sonatype.org --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b5b67e42f..f2ad5055f 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,11 @@ ossrh - https://oss.sonatype.org/content/repositories/snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -545,7 +545,7 @@ true ossrh - https://oss.sonatype.org/ + https://s01.oss.sonatype.org/ true 10 From 07d9fff828c213ecc4ce1e5e37029cbb282c0bf3 Mon Sep 17 00:00:00 2001 From: Pascal Kesseli Date: Sun, 6 Aug 2023 13:32:50 +0200 Subject: [PATCH 547/713] #787 Get all versions in multi-release JAR Allow retrieving all resource versions in a multi-release JAR. --- .../java/io/github/classgraph/ClassGraph.java | 29 +++++++++- .../classgraph/ClasspathElementDir.java | 2 +- .../classgraph/ClasspathElementModule.java | 2 +- .../classgraph/ClasspathElementZip.java | 2 +- .../fastzipfilereader/FastZipEntry.java | 4 +- .../fastzipfilereader/LogicalZipFile.java | 10 ++-- .../fastzipfilereader/NestedJarHandler.java | 2 +- .../github/classgraph/scanspec/ScanSpec.java | 3 ++ .../features/MultiReleaseJarTest.java | 53 +++++++++++++++++++ 9 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index c29683cfc..4a7f09cce 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -207,12 +207,14 @@ public ClassGraph enableAllInfo() { } /** - * Enables the scanning of classfiles, producing {@link ClassInfo} objects in the {@link ScanResult}. + * Enables the scanning of classfiles, producing {@link ClassInfo} objects in the {@link ScanResult}. Implicitly + * disables {@link #enableMultiReleaseVersions()}. * * @return this (for method chaining). */ public ClassGraph enableClassInfo() { scanSpec.enableClassInfo = true; + scanSpec.enableMultiReleaseVersions = false; return this; } @@ -1429,6 +1431,31 @@ public ClassGraph enableMemoryMapping() { return this; } + /** + * If true, provide all versions of a multi-release resource using their multi-release path prefix, instead of + * just the one the running JVM would select. Implicitly disables {@link #enableClassInfo()} and all features + * depending on it. + * + * @return this (for method chaining). + */ + public ClassGraph enableMultiReleaseVersions() { + scanSpec.enableMultiReleaseVersions = true; + + scanSpec.enableClassInfo = false; + scanSpec.ignoreClassVisibility = false; + scanSpec.enableMethodInfo = false; + scanSpec.ignoreMethodVisibility = false; + scanSpec.enableFieldInfo = false; + scanSpec.ignoreFieldVisibility = false; + scanSpec.enableStaticFinalFieldConstantInitializerValues = false; + scanSpec.enableAnnotationInfo = false; + scanSpec.enableInterClassDependencies = false; + scanSpec.disableRuntimeInvisibleAnnotations = false; + scanSpec.enableExternalClasses = false; + scanSpec.enableSystemJarsAndModules = false; + return this; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index dd14e4324..0fd825dec 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -350,7 +350,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Ignore versioned sections in exploded jars -- they are only supposed to be used in jars. // TODO: is it necessary to support multi-versioned exploded jars anyway? If so, all the paths in a // directory classpath entry will have to be pre-scanned and masked, as happens in ClasspathElementZip. - if (dirRelativePathStr.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions && dirRelativePathStr.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (log != null) { log.log("Found unexpected nested versioned entry in directory classpath element -- skipping: " + dirRelativePathStr); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 83d54b2b5..e8c6eb702 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -330,7 +330,7 @@ void scanPaths(final LogNode log) { // contain a path like "META-INF/versions/{version}/META-INF/versions/{version}/", which cannot // be valid (META-INF should only ever exist in the module root), and the nested versioned section // should be ignored. - if (relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (subLog != null) { subLog.log( "Found unexpected nested versioned entry in module -- skipping: " + relativePath); diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 919144081..6add0a9a8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -513,7 +513,7 @@ void scanPaths(final LogNode log) { // jar, in which case zipEntry.entryNameUnversioned has the version prefix stripped, or this is an // unversioned jar (e.g. the multi-version flag is not set in the manifest file) and there are some // spurious files in a multi-version path (in which case, they should be ignored). - if (relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (subLog != null) { if (VersionFinder.JAVA_MAJOR_VERSION < 9) { subLog.log("Skipping versioned entry in jar, because JRE version " diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java index 08936bed6..11afcfbfd 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -111,7 +111,7 @@ public class FastZipEntry implements Comparable { FastZipEntry(final LogicalZipFile parentLogicalZipFile, final long locHeaderPos, final String entryName, final boolean isDeflated, final long compressedSize, final long uncompressedSize, final long lastModifiedTimeMillis, final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, - final int fileAttributes) { + final int fileAttributes, final boolean enableMultiReleaseVersions) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; this.entryName = entryName; @@ -159,7 +159,7 @@ public class FastZipEntry implements Comparable { if (entryVersion < 9 || entryVersion > VersionFinder.JAVA_MAJOR_VERSION) { entryVersion = 8; } - if (entryVersion > 8) { + if (!enableMultiReleaseVersions && entryVersion > 8) { // Strip version path prefix entryNameWithoutVersionPrefix = entryName.substring(nextSlashIdx + 1); // For META-INF/versions/{versionInt}/META-INF/*, don't strip version prefix: diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java index 8d3a96d99..1281dd049 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -83,6 +83,9 @@ public class LogicalZipFile extends ZipFileSlice { /** If true, this is a JRE jar. */ public boolean isJREJar; + /** If true, multi-release versions should not be stripped in resource names. */ + private final boolean enableMultiReleaseVersions; + // ------------------------------------------------------------------------------------------------------------- /** {@code "META_INF/"}. */ @@ -148,9 +151,10 @@ public class LogicalZipFile extends ZipFileSlice { * @throws InterruptedException * if the thread was interrupted. */ - LogicalZipFile(final ZipFileSlice zipFileSlice, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException, InterruptedException { + LogicalZipFile(final ZipFileSlice zipFileSlice, final NestedJarHandler nestedJarHandler, final LogNode log, + final boolean enableMultiReleaseVersions) throws IOException, InterruptedException { super(zipFileSlice); + this.enableMultiReleaseVersions = enableMultiReleaseVersions; readCentralDirectory(nestedJarHandler, log); } @@ -780,7 +784,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, compressedSize, uncompressedSize, lastModifiedMillis, lastModifiedTimeMSDOS, - lastModifiedDateMSDOS, fileAttributes); + lastModifiedDateMSDOS, fileAttributes, enableMultiReleaseVersions); entries.add(entry); // Record manifest entry diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index d31cf8354..612a8e56e 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -145,7 +145,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode log) throws IOException, InterruptedException { // Read the central directory for the zipfile - return new LogicalZipFile(zipFileSlice, NestedJarHandler.this, log); + return new LogicalZipFile(zipFileSlice, NestedJarHandler.this, log, scanSpec.enableMultiReleaseVersions); } }; diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java index 0c0b802ef..39e06b28c 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -258,6 +258,9 @@ public class ScanSpec { /** If true, use a {@link MappedByteBuffer} rather than the {@link FileChannel} API to access file content. */ public boolean enableMemoryMapping; + /** If true, all multi-release versions of a resource are found. */ + public boolean enableMultiReleaseVersions; + // ------------------------------------------------------------------------------------------------------------- /** Constructor for deserialization. */ diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index c573b0b63..88815b801 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -1,6 +1,7 @@ package io.github.classgraph.features; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -79,4 +80,56 @@ public void multiReleaseVersioningOfResources() throws Exception { } } } + + /** + * Loading all versions of multi release class and text resources with `enableMultiReleaseVersions`. + * + * @throws Exception + * the exception + */ + @Test + public void enableMultiReleaseVersions() throws Exception { + // Multi-release jar sections are ignored by ClassGraph if JDK<9 + if (VersionFinder.JAVA_MAJOR_VERSION >= 9) { + try (ScanResult scanResult = new ClassGraph() + .overrideClassLoaders(new URLClassLoader(new URL[] { jarURL })).enableMultiReleaseVersions() + .scan()) { + final ResourceList java8ClassResource = scanResult.getResourcesWithPath("mrj/Cls.class"); + assertThat(java8ClassResource).hasSize(1); + final ResourceList java9ClassResource = scanResult.getResourcesWithPath("META-INF/versions/9/mrj/Cls.class"); + assertThat(java9ClassResource).hasSize(1); + assertThat(java8ClassResource.get(0).load()).isNotEqualTo(java9ClassResource.get(0).load()); + + final ResourceList java8Resource = scanResult.getResourcesWithPath("resource.txt"); + assertThat(java8Resource.size()).isEqualTo(1); + java8Resource.forEachByteArrayThrowingIOException( + (resource, byteArray) -> assertThat(new String(byteArray).trim()).isEqualTo("8")); + final ResourceList java9Resource = scanResult.getResourcesWithPath("META-INF/versions/9/resource.txt"); + assertThat(java9Resource.size()).isEqualTo(1); + java9Resource.forEachByteArrayThrowingIOException( + (resource, byteArray) -> assertThat(new String(byteArray).trim()).isEqualTo("9")); + } + } + } + + /** + * `enableMultiReleaseVersions` does not make sense with class info and should disable it. + * + * @throws Exception + * the exception + */ + @Test + public void enableMultiReleaseVersionsWithClassInfo() throws Exception { + // Multi-release jar sections are ignored by ClassGraph if JDK<9 + if (VersionFinder.JAVA_MAJOR_VERSION >= 9) { + try (ScanResult scanResult = new ClassGraph() + .overrideClassLoaders(new URLClassLoader(new URL[] { jarURL })).enableAllInfo() + .enableMultiReleaseVersions().scan()) { + final ResourceList java8ClassResource = scanResult.getResourcesWithPath("mrj/Cls.class"); + assertThat(java8ClassResource).hasSize(1); + assertThatThrownBy(() -> scanResult.getClassInfo("mrj.Cls")) + .isInstanceOfAny(IllegalArgumentException.class); + } + } + } } From 06ca78ce0bda0f55cd4d85a6e1bea96288019a0d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 13:38:17 -0600 Subject: [PATCH 548/713] Source > Cleanup --- .../java/io/github/classgraph/ClasspathElementDir.java | 3 ++- .../java/io/github/classgraph/ClasspathElementModule.java | 3 ++- .../java/io/github/classgraph/ClasspathElementZip.java | 3 ++- .../classgraph/fastzipfilereader/NestedJarHandler.java | 3 ++- .../github/classgraph/features/MultiReleaseJarTest.java | 8 +++++--- .../github/classgraph/issues/issue780/Issue780Test.java | 7 ++++--- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 0fd825dec..74b4a1e8f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -350,7 +350,8 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Ignore versioned sections in exploded jars -- they are only supposed to be used in jars. // TODO: is it necessary to support multi-versioned exploded jars anyway? If so, all the paths in a // directory classpath entry will have to be pre-scanned and masked, as happens in ClasspathElementZip. - if (!scanSpec.enableMultiReleaseVersions && dirRelativePathStr.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions + && dirRelativePathStr.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (log != null) { log.log("Found unexpected nested versioned entry in directory classpath element -- skipping: " + dirRelativePathStr); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index e8c6eb702..45513732f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -330,7 +330,8 @@ void scanPaths(final LogNode log) { // contain a path like "META-INF/versions/{version}/META-INF/versions/{version}/", which cannot // be valid (META-INF should only ever exist in the module root), and the nested versioned section // should be ignored. - if (!scanSpec.enableMultiReleaseVersions && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions + && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (subLog != null) { subLog.log( "Found unexpected nested versioned entry in module -- skipping: " + relativePath); diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 6add0a9a8..7393ffa68 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -513,7 +513,8 @@ void scanPaths(final LogNode log) { // jar, in which case zipEntry.entryNameUnversioned has the version prefix stripped, or this is an // unversioned jar (e.g. the multi-version flag is not set in the manifest file) and there are some // spurious files in a multi-version path (in which case, they should be ignored). - if (!scanSpec.enableMultiReleaseVersions && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (!scanSpec.enableMultiReleaseVersions + && relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (subLog != null) { if (VersionFinder.JAVA_MAJOR_VERSION < 9) { subLog.log("Skipping versioned entry in jar, because JRE version " diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 612a8e56e..5c38547a4 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -145,7 +145,8 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode log) throws IOException, InterruptedException { // Read the central directory for the zipfile - return new LogicalZipFile(zipFileSlice, NestedJarHandler.this, log, scanSpec.enableMultiReleaseVersions); + return new LogicalZipFile(zipFileSlice, NestedJarHandler.this, log, + scanSpec.enableMultiReleaseVersions); } }; diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index 88815b801..7ca13b4ab 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -96,7 +96,8 @@ public void enableMultiReleaseVersions() throws Exception { .scan()) { final ResourceList java8ClassResource = scanResult.getResourcesWithPath("mrj/Cls.class"); assertThat(java8ClassResource).hasSize(1); - final ResourceList java9ClassResource = scanResult.getResourcesWithPath("META-INF/versions/9/mrj/Cls.class"); + final ResourceList java9ClassResource = scanResult + .getResourcesWithPath("META-INF/versions/9/mrj/Cls.class"); assertThat(java9ClassResource).hasSize(1); assertThat(java8ClassResource.get(0).load()).isNotEqualTo(java9ClassResource.get(0).load()); @@ -104,7 +105,8 @@ public void enableMultiReleaseVersions() throws Exception { assertThat(java8Resource.size()).isEqualTo(1); java8Resource.forEachByteArrayThrowingIOException( (resource, byteArray) -> assertThat(new String(byteArray).trim()).isEqualTo("8")); - final ResourceList java9Resource = scanResult.getResourcesWithPath("META-INF/versions/9/resource.txt"); + final ResourceList java9Resource = scanResult + .getResourcesWithPath("META-INF/versions/9/resource.txt"); assertThat(java9Resource.size()).isEqualTo(1); java9Resource.forEachByteArrayThrowingIOException( (resource, byteArray) -> assertThat(new String(byteArray).trim()).isEqualTo("9")); @@ -128,7 +130,7 @@ public void enableMultiReleaseVersionsWithClassInfo() throws Exception { final ResourceList java8ClassResource = scanResult.getResourcesWithPath("mrj/Cls.class"); assertThat(java8ClassResource).hasSize(1); assertThatThrownBy(() -> scanResult.getClassInfo("mrj.Cls")) - .isInstanceOfAny(IllegalArgumentException.class); + .isInstanceOfAny(IllegalArgumentException.class); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java b/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java index f557fe6da..5e9ba2d5b 100644 --- a/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java +++ b/src/test/java/io/github/classgraph/issues/issue780/Issue780Test.java @@ -1,10 +1,11 @@ package io.github.classgraph.issues.issue780; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; public class Issue780Test { /** From 4cefb8a642777eabb7d18fc09174e6315dc4eecf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:01:03 -0600 Subject: [PATCH 549/713] [maven-release-plugin] prepare release classgraph-4.8.162 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f2ad5055f..850dac4be 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.162-SNAPSHOT + 4.8.162 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.161 + classgraph-4.8.162 From f0a364a60f5d1a724fc1154cda157f1cd69f3d12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:01:06 -0600 Subject: [PATCH 550/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 850dac4be..63f2945ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.162 + 4.8.163-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 12aa23f3d5bc8ec2b7aa76f981da4dad0456ddd3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:07:53 -0600 Subject: [PATCH 551/713] Update Javadoc --- src/main/java/io/github/classgraph/ModuleRef.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index a6496280a..97c09ab3f 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -79,6 +79,8 @@ public class ModuleRef implements Comparable { * The module reference, of JPMS type ModuleReference. * @param moduleLayer * The module layer, of JPMS type ModuleLayer + * @param reflectionUtils + * The ReflectionUtils instance. */ public ModuleRef(final Object moduleReference, final Object moduleLayer, final ReflectionUtils reflectionUtils) { From 9b5fac18de33b8d46d6f446571de627dd8f52f03 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:12:24 -0600 Subject: [PATCH 552/713] Go back to oss.sonatype.org --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 63f2945ec..3c1055dba 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,11 @@ ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots + https://oss.sonatype.org/content/repositories/snapshots ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -545,7 +545,7 @@ true ossrh - https://s01.oss.sonatype.org/ + https://oss.sonatype.org/ true 10 From 8ba4d60c00fd8b6deac4f1f225b3a931055986e5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:13:27 -0600 Subject: [PATCH 553/713] Bump version code back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c1055dba..4bb7ac0ab 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.163-SNAPSHOT + 4.8.162-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 11015652268d7c3b48ef17fd8aa92386bebf55c4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:17:48 -0600 Subject: [PATCH 554/713] [maven-release-plugin] prepare release classgraph-4.8.162 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4bb7ac0ab..a8486ae07 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.162-SNAPSHOT + 4.8.162 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 5f94ed6f590fa52047814eb92e67f23aa410019e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 7 Aug 2023 14:19:36 -0600 Subject: [PATCH 555/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8486ae07..3c1055dba 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.162 + 4.8.163-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 4100b1110eb6483346cf01e3225cd768b32ca2d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 03:11:01 +0000 Subject: [PATCH 556/713] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/codeql.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39408b08a..ed294720d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6408ddfdd..c526d80b0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 From e68c6741fd50f68c8a957f36c58f2a4a0bed3108 Mon Sep 17 00:00:00 2001 From: Clayton Walker Date: Fri, 8 Sep 2023 13:38:39 -0400 Subject: [PATCH 557/713] Fix patched module resource lookup --- .../classgraph/ClasspathElementModule.java | 14 ++++++++++ .../github/classgraph/ModuleReaderProxy.java | 26 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 45513732f..2912c0323 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -183,6 +183,20 @@ ClassfileReader openClassfile() throws IOException { return new ClassfileReader(open(), this); } + @Override + public URI getURI() { + try { + ModuleReaderProxy localModuleReaderProxy = moduleReaderProxyRecycler.acquire(); + try { + return localModuleReaderProxy.find(resourcePath); + } finally { + moduleReaderProxyRecycler.recycle(localModuleReaderProxy); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override public InputStream open() throws IOException { if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index 7819fdbf7..f866edec4 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -31,6 +31,7 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.nio.ByteBuffer; import java.util.List; @@ -183,4 +184,27 @@ public void release(final ByteBuffer byteBuffer) { reflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "release", ByteBuffer.class, byteBuffer); } -} \ No newline at end of file + + /** + * Use the proxied ModuleReader to find the named resource as a URI. + * + * @param path + * The path to the resource to open. + * @return A {@link URI} for the resource. + * @throws SecurityException + * If the module cannot be accessed. + */ + public URI find(final String path) { + final Object /* Optional */ optionalURI = reflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "find", String.class, + path); + if (optionalURI == null) { + throw new IllegalArgumentException("Got null result from ModuleReader#find(String)"); + } + final URI uri = (URI) reflectionUtils.invokeMethod(/* throwException = */ true, + optionalURI, "get"); + if (uri == null) { + throw new IllegalArgumentException("Got null result from ModuleReader#find(String).get()"); + } + return uri; + } +} From 0a7b2fad763d042e08ba2f07fde38ada04e557c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 12 Oct 2023 15:38:44 -0600 Subject: [PATCH 558/713] Work around JDK 17 bug (#797) --- .classpath | 19 ++++++++++- pom.xml | 18 +++++++---- .../java/io/github/classgraph/ClassInfo.java | 32 +++++++++++++++---- .../java/io/github/classgraph/Classfile.java | 11 +++++++ .../java/io/github/classgraph/FieldInfo.java | 17 ++++++---- .../classgraph/issues/issue797/Bar.java | 11 +++++++ .../issues/issue797/Issue797Test.java | 31 ++++++++++++++++++ 7 files changed, 119 insertions(+), 20 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue797/Bar.java create mode 100644 src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java diff --git a/.classpath b/.classpath index 1234d88f1..21d30f026 100644 --- a/.classpath +++ b/.classpath @@ -32,7 +32,7 @@ - + @@ -43,5 +43,22 @@ + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 3c1055dba..1da5ec09b 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,12 @@ 1.2 test + + jakarta.validation + jakarta.validation-api + 3.0.2 + test + @@ -408,9 +414,9 @@ 7 7 - - 8 - 8 + + 21 + 21 false -Xlint:all @@ -427,9 +433,9 @@ UTF-8 - - 8 - 8 + + 21 + 21 -parameters diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 02221f4ee..c9533be50 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -3401,9 +3401,10 @@ public int hashCode() { */ @Override protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + final boolean initialBufEmpty = buf.length() == 0; if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { - if (buf.length() > 0) { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } annotation.toString(useSimpleNames, buf); @@ -3423,15 +3424,32 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { } else { // Non-generic classes TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); - if (buf.length() > 0) { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } - buf.append(isRecord() ? "record " // - : isEnum() ? "enum " // - : isAnnotation() ? "@interface " // - : isInterface() ? "interface " // - : "class "); + // Don't put class type in extends/implements clauses + if (initialBufEmpty) { + buf.append(isRecord() ? "record " // + : isEnum() ? "enum " // + : isAnnotation() ? "@interface " // + : isInterface() ? "interface " // + : "class "); + } buf.append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); + if (isRecord) { + // Add params, if this is a record class + buf.append('('); + boolean isFirstParam = true; + for (final FieldInfo fieldInfo : getFieldInfo()) { + if (!isFirstParam) { + buf.append(", "); + } else { + isFirstParam = false; + } + fieldInfo.toString(/* useModifiers = */ false, /* useSimpleNames = */ false, buf); + } + buf.append(')'); + } final ClassInfo superclass = getSuperclass(); if (superclass != null && !superclass.getName().equals("java.lang.Object")) { buf.append(" extends "); diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 8b761c5d9..4fac237a4 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1561,6 +1561,17 @@ private void readMethods() throws IOException, ClassfileFormatException { boundIndex = reader.readUnsignedByte(); formalParameterIndex = -1; throwsTypeIndex = -1; + } else if (targetType == 0x13) { + // Type in field or record component declaration + // (empty target) + // This target_type is not supposed to be added to methods, but it seems + // that the JDK 17 compiler is buggy, and adds this target_type to the + // methods of records anyway (#797). Therefore, accept this, but ignore + // it (the same target_type should also be added to the fields of records). + typeParameterIndex = -1; + boundIndex = -1; + formalParameterIndex = -1; + throwsTypeIndex = -1; } else if (targetType == 0x14) { // Return type of method, or type of newly constructed object // (empty target) diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 6722d06d3..89edf91b2 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -392,25 +392,25 @@ public int compareTo(final FieldInfo other) { // ------------------------------------------------------------------------------------------------------------- - @Override - protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + void toString(final boolean includeModifiers, final boolean useSimpleNames, final StringBuilder buf) { if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { - if (buf.length() > 0) { + // There can be a paren in the previous position if this field is a record parameter + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } annotation.toString(useSimpleNames, buf); } } - if (modifiers != 0) { - if (buf.length() > 0) { + if (modifiers != 0 && includeModifiers) { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } TypeUtils.modifiersToString(modifiers, ModifierType.FIELD, /* ignored */ false, buf); } - if (buf.length() > 0) { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } final TypeSignature typeSig = getTypeSignatureOrTypeDescriptor(); @@ -432,4 +432,9 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { } } } + + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + toString(true, useSimpleNames, buf); + } } diff --git a/src/test/java/io/github/classgraph/issues/issue797/Bar.java b/src/test/java/io/github/classgraph/issues/issue797/Bar.java new file mode 100644 index 000000000..53b47a503 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue797/Bar.java @@ -0,0 +1,11 @@ +package io.github.classgraph.issues.issue797; + +import java.util.List; + +import jakarta.validation.constraints.NotNull; + +public record Bar(String baz, List<@NotNull String> value) { + public Bar { + baz = ""; + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java b/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java new file mode 100644 index 000000000..10f4ee6a6 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java @@ -0,0 +1,31 @@ +package io.github.classgraph.issues.issue797; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; + +public class Issue797Test { + /** + * Issue 797. + */ + @Test + public void getResourcesWithPathShouldNeverReturnNull() { + try (ScanResult result = new ClassGraph().enableAllInfo().acceptClasses(Bar.class.getName()).scan()) { + final ClassInfo bar = result.getClassInfo(Bar.class.getName()); + assertThat(bar.toString()).isEqualTo( + "public final record io.github.classgraph.issues.issue797.Bar(" + "java.lang.String baz, " + + "java.util.List<@jakarta.validation.constraints.NotNull java.lang.String> value) " + + "extends java.lang.Record"); + final MethodInfo baz = bar.getMethodInfo("baz").get(0); + assertThat(baz.toString()).isEqualTo("public java.lang.String baz()"); + final MethodInfo value = bar.getMethodInfo("value").get(0); + assertThat(value.toString()).isEqualTo( + "public java.util.List<@jakarta.validation.constraints.NotNull java.lang.String> " + "value()"); + } + } +} From 84b76f5375ddceb60a995415a8bb83b6cd64fe16 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 02:40:35 -0600 Subject: [PATCH 559/713] Compile tests for JDK 17 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index cf1982e6d..8ce92061c 100644 --- a/pom.xml +++ b/pom.xml @@ -415,8 +415,8 @@ 7 7 - 21 - 21 + 17 + 17 false -Xlint:all @@ -434,8 +434,8 @@ UTF-8 - 21 - 21 + 17 + 17 -parameters From 87b2d08dd6499546c132fea63b2722765b45e157 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 02:49:36 -0600 Subject: [PATCH 560/713] Update codeql.yml --- .github/workflows/codeql.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c526d80b0..0cd5877af 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,6 +26,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Setup Java SDK + uses: actions/setup-java@v1.3.0 + with: + java-version: 17 + - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: From 0e32b49c260a9e5f8afefe14b326546dacccd37e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 02:51:13 -0600 Subject: [PATCH 561/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed294720d..2b0967468 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18-ea' ] + java: [ '17', '18', '19', '20', '21' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From 4166fc02b71873b1a99140d09ac61e7badb3ac93 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 02:58:10 -0600 Subject: [PATCH 562/713] Update codeql.yml --- .github/workflows/codeql.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0cd5877af..dba57edd0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,8 +27,9 @@ jobs: uses: actions/checkout@v4 - name: Setup Java SDK - uses: actions/setup-java@v1.3.0 + uses: actions/setup-java@v3 with: + distribution: 'oracle' java-version: 17 - name: Initialize CodeQL From 68804d35aa7c7d71c977d4e7f9df1c65c6a42366 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:17:02 -0600 Subject: [PATCH 563/713] Restore JDK 8 compat --- .classpath | 2 +- pom.xml | 8 ++++---- .../github/classgraph/issues/issue797/Bar.java | 11 ----------- .../classgraph/issues/issue797/Issue797Test.java | 8 ++++++-- src/test/resources/issue797.jar | Bin 0 -> 1434 bytes 5 files changed, 11 insertions(+), 18 deletions(-) delete mode 100644 src/test/java/io/github/classgraph/issues/issue797/Bar.java create mode 100644 src/test/resources/issue797.jar diff --git a/.classpath b/.classpath index 21d30f026..dff964fcf 100644 --- a/.classpath +++ b/.classpath @@ -32,7 +32,7 @@ - + diff --git a/pom.xml b/pom.xml index 8ce92061c..0632dd80b 100644 --- a/pom.xml +++ b/pom.xml @@ -415,8 +415,8 @@ 7 7 - 17 - 17 + 8 + 8 false -Xlint:all @@ -434,8 +434,8 @@ UTF-8 - 17 - 17 + 8 + 8 -parameters diff --git a/src/test/java/io/github/classgraph/issues/issue797/Bar.java b/src/test/java/io/github/classgraph/issues/issue797/Bar.java deleted file mode 100644 index 53b47a503..000000000 --- a/src/test/java/io/github/classgraph/issues/issue797/Bar.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.classgraph.issues.issue797; - -import java.util.List; - -import jakarta.validation.constraints.NotNull; - -public record Bar(String baz, List<@NotNull String> value) { - public Bar { - baz = ""; - } -} diff --git a/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java b/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java index 10f4ee6a6..6ec88436b 100644 --- a/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java +++ b/src/test/java/io/github/classgraph/issues/issue797/Issue797Test.java @@ -2,6 +2,8 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.URL; + import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -15,8 +17,10 @@ public class Issue797Test { */ @Test public void getResourcesWithPathShouldNeverReturnNull() { - try (ScanResult result = new ClassGraph().enableAllInfo().acceptClasses(Bar.class.getName()).scan()) { - final ClassInfo bar = result.getClassInfo(Bar.class.getName()); + // Jar is precompiled, since it uses a JDK 17 feature (records) + final URL url = Issue797Test.class.getResource("/issue797.jar"); + try (ScanResult result = new ClassGraph().overrideClasspath(url).enableAllInfo().scan()) { + final ClassInfo bar = result.getClassInfo("io.github.classgraph.issues.issue797.Bar"); assertThat(bar.toString()).isEqualTo( "public final record io.github.classgraph.issues.issue797.Bar(" + "java.lang.String baz, " + "java.util.List<@jakarta.validation.constraints.NotNull java.lang.String> value) " diff --git a/src/test/resources/issue797.jar b/src/test/resources/issue797.jar new file mode 100644 index 0000000000000000000000000000000000000000..df7f1672ca585e6e5ac2a3f29c3149a0d38bf50a GIT binary patch literal 1434 zcmWIWW@Zs#U|`^2D3J6G=S(vV(Pm~~$YN(;&|{Ec$jsMI&n(F(P0~-!Nh~f-FG?)P z(9bL`E=?_lQ0A8A`c8>OdSL0$5Kac>!L=xEbJ)ouT4ee zh^vB35l2NqOTij*C5|Ew&rla`4Y@CwlYCT}Pv_m4yVR?0GJlY2swn?IhWaU~=kt^p zom$#cs&@VUy{mec_0PZ0zlb+nkLX|Asi-TvStIo1`I(j7)o~Y}KAog#dtN-Q;>@|T zv$XH@DxbQr?>PU?%);Fnd7XP6i%BS1)*YE}YF^~FsoHNJ**L$M5WPgi7(N;s&qKxSJ9g8lYVcE&iuW;~-G?D7>tfZQYmiB785%qVeMb3SI?$s6s}(~-?HKk@7cMA27R7wFJ~9zU&!dtx7)tr z&%0aG>QB7wwCO#h?wcz$qL8y33{7x_NZYqXM?FYRuB{o<+HTTCBEGI8(XsWXxP)3xQ|hfB++ zAH1<4_aWQG7V+aoxgWQl>Q0`TxMHLBSNjq*#XpUQlW(2jDBa7cxp#%i(l4_v?Nz;? zR@i+`B|=@VB`p)w^3uI_N!W%r>cEGt5}warIRA$&y)#Wr^S%$(N#$3c_eP(zj(YbGjzk4hH2+CSNj?OlTR@$1JIOKrI9;8%8} zHfxEIM6&pW?~{^ztnyadOrI+7<)fa#oM z#SVyeEs8p+llGB0z?+dtju}_+BmoQ&2w2hxqDd{RSRsWKTCgD7i5dP7I~jqtEoqz# fEZc~-6A~LhE3w2xfHx}}$OBA3_#8;jWdZR3XYf!N literal 0 HcmV?d00001 From 9da60a2b11d9295ba3479850c136e4d0446ddf7f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:19:30 -0600 Subject: [PATCH 564/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0967468..d5f5bd53b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '17', '18', '19', '20', '21' ] + java: [ '8', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From fefff7ef492e9c963807243ff9052013eccf62b3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:19:57 -0600 Subject: [PATCH 565/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5f5bd53b..6028b974d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - distribution: 'zulu' + distribution: 'oracle' java-version: ${{ matrix.java }} - name: print Java version run: java -version From 55deed4e0d98b13cbde3d882182de0041dfca589 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:20:20 -0600 Subject: [PATCH 566/713] Update codeql.yml --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index dba57edd0..1b82ecba8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'oracle' - java-version: 17 + java-version: 8 - name: Initialize CodeQL uses: github/codeql-action/init@v2 From 32e6e56d53d910e470ffa098d8c68d9ea56b6d9a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:23:26 -0600 Subject: [PATCH 567/713] Update codeql.yml --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1b82ecba8..24bf419db 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: - name: Setup Java SDK uses: actions/setup-java@v3 with: - distribution: 'oracle' + distribution: 'zulu' java-version: 8 - name: Initialize CodeQL From c5f067dc7f25651f5d0ef76ec3dd27623107bd27 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:23:43 -0600 Subject: [PATCH 568/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6028b974d..d5f5bd53b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - distribution: 'oracle' + distribution: 'zulu' java-version: ${{ matrix.java }} - name: print Java version run: java -version From cbba0acecc5b5b2503373af3e0e69473c2bec91c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:32:39 -0600 Subject: [PATCH 569/713] Chain exceptions --- .../classgraph/reflection/ReflectionUtils.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 14815a0e0..095ff2fd6 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -109,7 +109,7 @@ public Object getFieldVal(final boolean throwException, final Object obj, final } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( - "Can't read field " + obj.getClass().getName() + "." + field.getName() + ": " + e); + "Can't read field " + obj.getClass().getName() + "." + field.getName(), e); } } return null; @@ -149,7 +149,7 @@ public Object getFieldVal(final boolean throwException, final Object obj, final } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( - "Can't read field " + obj.getClass().getName() + "." + fieldName + ": " + e); + "Can't read field " + obj.getClass().getName() + "." + fieldName, e); } } return null; @@ -189,7 +189,7 @@ public Object getStaticFieldVal(final boolean throwException, final Class cls } catch (final Throwable e) { if (throwException) { throw new IllegalArgumentException( - "Can't read field " + cls.getName() + "." + fieldName + ": " + e); + "Can't read field " + cls.getName() + "." + fieldName, e); } } return null; @@ -228,7 +228,7 @@ public Object invokeMethod(final boolean throwException, final Object obj, final return reflectionDriver.invokeMethod(obj, reflectionDriver.findInstanceMethod(obj, methodName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked", e); } return null; } @@ -272,7 +272,7 @@ public Object invokeMethod(final boolean throwException, final Object obj, final param); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked", e); } return null; } @@ -310,7 +310,7 @@ public Object invokeStaticMethod(final boolean throwException, final Class cl return reflectionDriver.invokeStaticMethod(reflectionDriver.findStaticMethod(cls, methodName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("Method \"" + methodName + "\" could not be invoked", e); } return null; } @@ -353,7 +353,7 @@ public Object invokeStaticMethod(final boolean throwException, final Class cl param); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException("method \"" + methodName + "\" could not be invoked: " + e); + throw new IllegalArgumentException("Fethod \"" + methodName + "\" could not be invoked", e); } return null; } From d73d25ea1214931cfe4ab617906686a4914c59f0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:37:35 -0600 Subject: [PATCH 570/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5f5bd53b..d29e376b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21' ] + java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18', '19' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From 9422b189a0a90f8df45afd9b8ba7e39bb2ad2f06 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:50:11 -0600 Subject: [PATCH 571/713] [maven-release-plugin] prepare release classgraph-4.8.163 --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0632dd80b..6e82cc5b4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.163-SNAPSHOT + 4.8.163 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.162 + classgraph-4.8.163 @@ -412,9 +412,10 @@ + 7 7 - + 8 8 false From 8b51126f4b329251d70fc4d184e6c032b64d8c9d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 13 Oct 2023 03:51:01 -0600 Subject: [PATCH 572/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e82cc5b4..01a933dde 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.163 + 4.8.164-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 92caa0fbc053c328f64280528dcc3bf8baed8ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 02:05:48 +0000 Subject: [PATCH 573/713] Bump org.ops4j.pax.url:pax-url-aether from 2.6.1 to 2.6.14 Bumps [org.ops4j.pax.url:pax-url-aether](https://github.com/ops4j/org.ops4j.pax.url) from 2.6.1 to 2.6.14. - [Commits](https://github.com/ops4j/org.ops4j.pax.url/compare/url-2.6.1...url-2.6.14) --- updated-dependencies: - dependency-name: org.ops4j.pax.url:pax-url-aether dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01a933dde..0cc01a759 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ org.ops4j.pax.url pax-url-aether - 2.6.1 + 2.6.14 test From e61284f10b764ef349a96aaeac5416c2f580360e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 02:05:58 +0000 Subject: [PATCH 574/713] Bump org.openjdk.jmh:jmh-core from 1.36 to 1.37 Bumps [org.openjdk.jmh:jmh-core](https://github.com/openjdk/jmh) from 1.36 to 1.37. - [Commits](https://github.com/openjdk/jmh/compare/1.36...1.37) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01a933dde..b6a2a2426 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ org.openjdk.jmh jmh-core - 1.36 + 1.37 test From fab00936b8448007cc4c7110a550dcbbdffc9b26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 02:22:10 +0000 Subject: [PATCH 575/713] Bump io.github.toolfactory:jvm-driver from 9.4.3 to 9.6.0 Bumps [io.github.toolfactory:jvm-driver](https://github.com/toolfactory/jvm-driver) from 9.4.3 to 9.6.0. - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-9.4.3...jvm-driver-9.6.0) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01a933dde..ed2d81e6f 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 9.4.3 + 9.6.0 true From 72c52de6f92967738b9bf90575824a245a7b1a17 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Mon, 23 Oct 2023 16:38:42 +0200 Subject: [PATCH 576/713] Handle paths with spaces and hashes especially with nested JARs With nested jars there are two different mechanisms that will be used as the path is not usable as a `java.nio.file.Path` instance. The first is trying to convert the resulting nested path - a path like `jar:file:....!/some/nested/path` - to a `URL` and if that should fail due to a `MalformedURLException` it is tried to convert the path to `URI`. If the URI fallback fails an IOException will be thrown and this eventually will bubble up and discard the whole classpath entry, resulting in a message like the following when enabling verbose output during scanning: ``` 2023-11-02T12:51:42.719+0100 ClassGraph -- Skipping invalid classpath entry .../spring-boot-fully-executable-jar.jar!/BOOT-INF/lib/... : java.io.IOException: Malformed URI: ... ``` Most of the time nothing will be discarded as most paths can be converted to a URL in the first step or at least succeed when converting to a URI. However for paths containing spaces and the hash symbol we can reach a case where both URL conversion and URI conversion fail and so the classpath entry is discarded even though all paths are valid and can be usable. Let us assume a Spring Boot Executable JAR that is located in a directory named `ci-build main #123` - which is a valid directory name on Windows and Linux. When ClassGraph reaches a nested library here it will construct the paths to the nested jars like `jar:file:!/`. So in this case we end up with something like `jar:file:/opt/ci-build main #123!/BOOT-INF/lib/my-lib.jar`. When ClassGraph reaches the conversion code it will first try to convert to a URL. This will fail with the following message: `java.net.MalformedURLException: no !/ in spec` If we then fallback to the URI conversion it will try to convert but as our path contains spaces this will also be rejected by an exception: `java.net.URISyntaxException: Illegal character in opaque part at index 66: jar:file:...` The index will point to the first space in the path that is converted. So we can construct nested paths that are neither valid `URL` instances nor valid `URI instances`. To solve this issue we introduce encoding for spaces when the path is handled as a url or multi-section path to ensure that conversion can succeed. This seems to also be what the `java.nio.file.Path` API does when asking for the resulting URI for the same path. So this commit encodes spaces as `%20` and hash symbols as `%23` when going into the URL/Multi-Section branch. Fixes #804 --- .../java/io/github/classgraph/Scanner.java | 3 + .../issues/issue804/Issue804Test.java | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 857f863ec..5b99befe1 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -402,6 +402,9 @@ private static Object normalizeClasspathEntry(final Object classpathEntryObj) th final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntStr).matches(); final boolean isMultiSection = classpathEntStr.contains("!"); if (isURL || isMultiSection) { + // Encode spaces and hash symbols in classpath entry as they potentially can be invalid when + // converted to a URL/URI + classpathEntStr = classpathEntStr.replace(" ", "%20").replace("#", "%23"); // Convert back to URL (or URI) if this has a URL scheme or if this is a multi-section // path (which needs the "jar:file:" scheme) if (!isURL) { diff --git a/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java b/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java new file mode 100644 index 000000000..372dc94d5 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java @@ -0,0 +1,66 @@ +package io.github.classgraph.issues.issue804; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue 804. + */ +public class Issue804Test { + + private static final String NESTED_EXAMPLE_CLASS = "org.springframework.util.ResourceUtils"; + + @Test + void scanningNestedJarsInPathsContainingSpacesShouldNeverFail(@TempDir Path tempDir) throws IOException { + Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces"); + + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } + + @Test + void scanningNestedJarsInPathsContainingHashesShouldNeverFail(@TempDir Path tempDir) throws IOException { + Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory-without-spaces#123"); + + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } + + @Test + void scanningNestedJarsInPathsContainingSpacesAndHashesShouldNeverFail(@TempDir Path tempDir) throws IOException { + Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces #123"); + + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } + + private Path createSpringBootJarInExampleDirectory(Path temporaryDirectory, String directoryName) + throws IOException { + Path directoryWithSpaces = temporaryDirectory.resolve(directoryName); + Files.createDirectories(directoryWithSpaces); + Path nestedJar = directoryWithSpaces.resolve("spring-boot-fully-executable-jar.jar"); + try (InputStream nestedJarsExample = Issue804Test.class.getClassLoader() + .getResourceAsStream("spring-boot-fully-executable-jar.jar")) { + Files.copy(nestedJarsExample, nestedJar); + } + return nestedJar; + } + + private ScanResult scanJar(Path targetJar) { + return new ClassGraph().enableClassInfo().overrideClasspath(targetJar.toUri()).scan(); + } + +} From a942bf644a7b488ba1d8ae791ca8ca3621da878d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 Nov 2023 12:51:07 -0600 Subject: [PATCH 577/713] Source > Cleanup --- .../java/io/github/classgraph/ClassInfo.java | 3 +- .../classgraph/ClasspathElementModule.java | 4 +- .../java/io/github/classgraph/FieldInfo.java | 3 +- .../github/classgraph/ModuleReaderProxy.java | 7 +- .../reflection/ReflectionUtils.java | 7 +- .../issues/issue804/Issue804Test.java | 73 ++++++++++--------- 6 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index c9533be50..2f61b7159 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -3404,7 +3404,8 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { final boolean initialBufEmpty = buf.length() == 0; if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { - if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' + && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } annotation.toString(useSimpleNames, buf); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 2912c0323..6af3660c7 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -186,13 +186,13 @@ ClassfileReader openClassfile() throws IOException { @Override public URI getURI() { try { - ModuleReaderProxy localModuleReaderProxy = moduleReaderProxyRecycler.acquire(); + final ModuleReaderProxy localModuleReaderProxy = moduleReaderProxyRecycler.acquire(); try { return localModuleReaderProxy.find(resourcePath); } finally { moduleReaderProxyRecycler.recycle(localModuleReaderProxy); } - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 89edf91b2..cf8bbb7e2 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -396,7 +396,8 @@ void toString(final boolean includeModifiers, final boolean useSimpleNames, fina if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { // There can be a paren in the previous position if this field is a record parameter - if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' && buf.charAt(buf.length() - 1) != '(') { + if (buf.length() > 0 && buf.charAt(buf.length() - 1) != ' ' + && buf.charAt(buf.length() - 1) != '(') { buf.append(' '); } annotation.toString(useSimpleNames, buf); diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index f866edec4..2ec6ec657 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -195,13 +195,12 @@ public void release(final ByteBuffer byteBuffer) { * If the module cannot be accessed. */ public URI find(final String path) { - final Object /* Optional */ optionalURI = reflectionUtils.invokeMethod(/* throwException = */ true, moduleReader, "find", String.class, - path); + final Object /* Optional */ optionalURI = reflectionUtils.invokeMethod(/* throwException = */ true, + moduleReader, "find", String.class, path); if (optionalURI == null) { throw new IllegalArgumentException("Got null result from ModuleReader#find(String)"); } - final URI uri = (URI) reflectionUtils.invokeMethod(/* throwException = */ true, - optionalURI, "get"); + final URI uri = (URI) reflectionUtils.invokeMethod(/* throwException = */ true, optionalURI, "get"); if (uri == null) { throw new IllegalArgumentException("Got null result from ModuleReader#find(String).get()"); } diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 095ff2fd6..176a85efa 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -148,8 +148,8 @@ public Object getFieldVal(final boolean throwException, final Object obj, final return reflectionDriver.getField(obj, reflectionDriver.findInstanceField(obj, fieldName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException( - "Can't read field " + obj.getClass().getName() + "." + fieldName, e); + throw new IllegalArgumentException("Can't read field " + obj.getClass().getName() + "." + fieldName, + e); } } return null; @@ -188,8 +188,7 @@ public Object getStaticFieldVal(final boolean throwException, final Class cls return reflectionDriver.getStaticField(reflectionDriver.findStaticField(cls, fieldName)); } catch (final Throwable e) { if (throwException) { - throw new IllegalArgumentException( - "Can't read field " + cls.getName() + "." + fieldName, e); + throw new IllegalArgumentException("Can't read field " + cls.getName() + "." + fieldName, e); } } return null; diff --git a/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java b/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java index 372dc94d5..c0f9cc890 100644 --- a/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java +++ b/src/test/java/io/github/classgraph/issues/issue804/Issue804Test.java @@ -18,49 +18,50 @@ */ public class Issue804Test { - private static final String NESTED_EXAMPLE_CLASS = "org.springframework.util.ResourceUtils"; + private static final String NESTED_EXAMPLE_CLASS = "org.springframework.util.ResourceUtils"; - @Test - void scanningNestedJarsInPathsContainingSpacesShouldNeverFail(@TempDir Path tempDir) throws IOException { - Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces"); + @Test + void scanningNestedJarsInPathsContainingSpacesShouldNeverFail(@TempDir final Path tempDir) throws IOException { + final Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces"); - try (ScanResult scanResult = scanJar(targetJar)) { - assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); - } - } + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } - @Test - void scanningNestedJarsInPathsContainingHashesShouldNeverFail(@TempDir Path tempDir) throws IOException { - Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory-without-spaces#123"); + @Test + void scanningNestedJarsInPathsContainingHashesShouldNeverFail(@TempDir final Path tempDir) throws IOException { + final Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory-without-spaces#123"); - try (ScanResult scanResult = scanJar(targetJar)) { - assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); - } - } + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } - @Test - void scanningNestedJarsInPathsContainingSpacesAndHashesShouldNeverFail(@TempDir Path tempDir) throws IOException { - Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces #123"); + @Test + void scanningNestedJarsInPathsContainingSpacesAndHashesShouldNeverFail(@TempDir final Path tempDir) + throws IOException { + final Path targetJar = createSpringBootJarInExampleDirectory(tempDir, "directory with spaces #123"); - try (ScanResult scanResult = scanJar(targetJar)) { - assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); - } - } + try (ScanResult scanResult = scanJar(targetJar)) { + assertThat(scanResult.getClassInfo(NESTED_EXAMPLE_CLASS)).isNotNull(); + } + } - private Path createSpringBootJarInExampleDirectory(Path temporaryDirectory, String directoryName) - throws IOException { - Path directoryWithSpaces = temporaryDirectory.resolve(directoryName); - Files.createDirectories(directoryWithSpaces); - Path nestedJar = directoryWithSpaces.resolve("spring-boot-fully-executable-jar.jar"); - try (InputStream nestedJarsExample = Issue804Test.class.getClassLoader() - .getResourceAsStream("spring-boot-fully-executable-jar.jar")) { - Files.copy(nestedJarsExample, nestedJar); - } - return nestedJar; - } + private Path createSpringBootJarInExampleDirectory(final Path temporaryDirectory, final String directoryName) + throws IOException { + final Path directoryWithSpaces = temporaryDirectory.resolve(directoryName); + Files.createDirectories(directoryWithSpaces); + final Path nestedJar = directoryWithSpaces.resolve("spring-boot-fully-executable-jar.jar"); + try (InputStream nestedJarsExample = Issue804Test.class.getClassLoader() + .getResourceAsStream("spring-boot-fully-executable-jar.jar")) { + Files.copy(nestedJarsExample, nestedJar); + } + return nestedJar; + } - private ScanResult scanJar(Path targetJar) { - return new ClassGraph().enableClassInfo().overrideClasspath(targetJar.toUri()).scan(); - } + private ScanResult scanJar(final Path targetJar) { + return new ClassGraph().enableClassInfo().overrideClasspath(targetJar.toUri()).scan(); + } } From 8360c03ebcd1f196a7101f4364aaab0bed206376 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 Nov 2023 12:56:06 -0600 Subject: [PATCH 578/713] [maven-release-plugin] prepare release classgraph-4.8.164 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 01a933dde..cebdac144 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.164-SNAPSHOT + 4.8.164 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.163 + classgraph-4.8.164 From 73fc284666864b65ef9051befbfb9785bec85798 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 Nov 2023 12:56:09 -0600 Subject: [PATCH 579/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cebdac144..3553ac97c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.164 + 4.8.165-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From a499338b86f29d86617426d44a57787a67b9c444 Mon Sep 17 00:00:00 2001 From: redmitry Date: Wed, 15 Nov 2023 20:58:55 +0100 Subject: [PATCH 580/713] add jdk.unsupported to the manifest --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 3553ac97c..2858731f1 100644 --- a/pom.xml +++ b/pom.xml @@ -500,6 +500,7 @@ javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + jdk.unsupported true From 617de838c65edec7a63e754543565766c98d513c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 Nov 2023 09:55:04 -0700 Subject: [PATCH 581/713] Add other module dependencies --- .project | 51 +- .vscode/settings.json | 3 + pom.xml | 18 +- .../java/io/github/classgraph/ScanResult.java | 656 ++++++++++++------ .../classgraph/fileslice/PathSlice.java | 73 +- 5 files changed, 511 insertions(+), 290 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.project b/.project index 0e6b5e6db..8b3ccf423 100644 --- a/.project +++ b/.project @@ -1,23 +1,34 @@ - ClassGraph - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - + ClassGraph + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + + + 1700088758021 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7b016a89f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2858731f1..2fc4c76a6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 io.github.classgraph @@ -31,7 +32,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.164 + classgraph-4.8.164 @@ -200,7 +201,7 @@ org.apache.maven.plugins maven-antrun-plugin - 3.1.0 + 3.1.0 org.apache.maven.plugins @@ -415,14 +416,14 @@ 7 7 - + 8 8 false - -Xlint:all - -Xlint:-options - -Werror + -Xlint:all + -Xlint:-options + -Werror @@ -498,9 +499,10 @@ javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + + java.xml,jdk.unsupported,java.management,java.logging - jdk.unsupported true diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 3b040e158..f115d42b9 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -66,24 +66,31 @@ import nonapi.io.github.classgraph.utils.LogNode; /** - * The result of a scan. You should assign a ScanResult in a try-with-resources block, or manually close it when you + * The result of a scan. You should assign a ScanResult in a try-with-resources + * block, or manually close it when you * have finished with the result of a scan. */ -public final class ScanResult implements Closeable, AutoCloseable { +public final class ScanResult implements Closeable { /** The order of raw classpath elements. */ private List rawClasspathEltOrderStrs; - /** The order of classpath elements, after inner jars have been extracted to temporary files, etc. */ + /** + * The order of classpath elements, after inner jars have been extracted to + * temporary files, etc. + */ private List classpathOrder; /** A list of all files that were found in accepted packages. */ private ResourceList allAcceptedResourcesCached; - /** The number of times {@link #getResourcesWithPath(String)} has been called. */ + /** + * The number of times {@link #getResourcesWithPath(String)} has been called. + */ private final AtomicInteger getResourcesWithPathCallCount = new AtomicInteger(); /** - * The map from path (relative to package root) to a list of {@link Resource} elements with the matching path. + * The map from path (relative to package root) to a list of {@link Resource} + * elements with the matching path. */ private Map pathToAcceptedResourcesCached; @@ -97,13 +104,19 @@ public final class ScanResult implements Closeable, AutoCloseable { private Map moduleNameToModuleInfo; /** - * The file, directory and jarfile resources timestamped during a scan, along with their timestamp at the time - * of the scan. For jarfiles, the timestamp represents the timestamp of all files within the jar. May be null, - * if this ScanResult object is the result of a call to ClassGraph#getUniqueClasspathElementsAsync(). + * The file, directory and jarfile resources timestamped during a scan, along + * with their timestamp at the time + * of the scan. For jarfiles, the timestamp represents the timestamp of all + * files within the jar. May be null, + * if this ScanResult object is the result of a call to + * ClassGraph#getUniqueClasspathElementsAsync(). */ private Map fileToLastModified; - /** If true, this {@link ScanResult} was produced by {@link ScanResult#fromJSON(String)}. */ + /** + * If true, this {@link ScanResult} was produced by + * {@link ScanResult#fromJSON(String)}. + */ private boolean isObtainedFromDeserialization; /** A custom ClassLoader that can load classes found during the scan. */ @@ -132,7 +145,8 @@ public final class ScanResult implements Closeable, AutoCloseable { private final WeakReference weakReference; /** - * The set of WeakReferences to non-closed ScanResult objects. Uses WeakReferences so that garbage collection is + * The set of WeakReferences to non-closed ScanResult objects. Uses + * WeakReferences so that garbage collection is * not blocked. (Bug #233) */ private static Set> nonClosedWeakReferences = Collections @@ -146,7 +160,10 @@ public final class ScanResult implements Closeable, AutoCloseable { /** The current serialization format. */ private static final String CURRENT_SERIALIZATION_FORMAT = "10"; - /** A class to hold a serialized ScanResult along with the ScanSpec that was used to scan. */ + /** + * A class to hold a serialized ScanResult along with the ScanSpec that was used + * to scan. + */ private static class SerializationFormat { /** The serialization format. */ public String format; @@ -178,17 +195,17 @@ public SerializationFormat() { * Constructor. * * @param serializationFormatStr - * the serialization format string + * the serialization format string * @param scanSpec - * the scan spec + * the scan spec * @param classInfo - * the list of all {@link ClassInfo} objects + * the list of all {@link ClassInfo} objects * @param packageInfo - * the list of all {@link PackageInfo} objects + * the list of all {@link PackageInfo} objects * @param moduleInfo - * the list of all {@link ModuleInfo} objects + * the list of all {@link ModuleInfo} objects * @param classpath - * the classpath as a list of URL strings + * the classpath as a list of URL strings */ public SerializationFormat(final String serializationFormatStr, final ScanSpec scanSpec, final List classInfo, final List packageInfo, @@ -206,16 +223,23 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // Shutdown hook init code /** - * Static initialization (warm up classloading), called when the ClassGraph class is initialized. + * Static initialization (warm up classloading), called when the ClassGraph + * class is initialized. */ 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. + // 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), reflectionUtils, /* log = */ null); } } @@ -224,28 +248,29 @@ static void init(final ReflectionUtils reflectionUtils) { // Constructor /** - * The result of a scan. Make sure you call complete() after calling the constructor. + * The result of a scan. Make sure you call complete() after calling the + * constructor. * * @param scanSpec - * the scan spec + * the scan spec * @param classpathOrder - * the classpath order + * the classpath order * @param rawClasspathEltOrderStrs - * the raw classpath element order + * the raw classpath element order * @param classpathFinder - * the {@link ClasspathFinder} + * the {@link ClasspathFinder} * @param classNameToClassInfo - * a map from class name to class info + * a map from class name to class info * @param packageNameToPackageInfo - * a map from package name to package info + * a map from package name to package info * @param moduleNameToModuleInfo - * a map from module name to module info + * a map from module name to module info * @param fileToLastModified - * a map from file to last modified time + * a map from file to last modified time * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @param topLevelLog - * the toplevel log + * the toplevel log */ ScanResult(final ScanSpec scanSpec, final List classpathOrder, final List rawClasspathEltOrderStrs, final ClasspathFinder classpathFinder, @@ -319,7 +344,8 @@ private void indexResourcesAndClassInfo(final LogNode log) { classInfo.setScanResult(this); } - // If inter-class dependencies are enabled, create placeholder ClassInfo objects for any referenced + // If inter-class dependencies are enabled, create placeholder ClassInfo objects + // for any referenced // classes that were not scanned if (scanSpec.enableInterClassDependencies) { for (final ClassInfo ci : new ArrayList<>(classNameToClassInfo.values())) { @@ -343,7 +369,8 @@ private void indexResourcesAndClassInfo(final LogNode log) { // Classpath / module path /** - * Returns the list of File objects for unique classpath elements (directories or jarfiles), in classloader + * Returns the list of File objects for unique classpath elements (directories + * or jarfiles), in classloader * resolution order. * * @return The unique classpath elements. @@ -363,10 +390,12 @@ public List getClasspathFiles() { } /** - * Returns all unique directories or zip/jarfiles on the classpath, in classloader resolution order, as a + * Returns all unique directories or zip/jarfiles on the classpath, in + * classloader resolution order, as a * classpath string, delineated with the standard path separator character. * - * @return a the unique directories and jarfiles on the classpath, in classpath resolution order, as a path + * @return a the unique directories and jarfiles on the classpath, in classpath + * resolution order, as a path * string. */ public String getClasspath() { @@ -401,8 +430,10 @@ public List getClasspathURIs() { } /** - * Returns an ordered list of unique classpath element and module URLs. Will skip any system modules or modules - * that are part of a jlink'd runtime image, since {@link URL} does not support the {@code jrt:} {@link URI} + * Returns an ordered list of unique classpath element and module URLs. Will + * skip any system modules or modules + * that are part of a jlink'd runtime image, since {@link URL} does not support + * the {@code jrt:} {@link URI} * scheme. * * @return The unique classpath element and module URLs. @@ -441,13 +472,18 @@ public List getModules() { } /** - * Get the module path info provided on the commandline with {@code --module-path}, {@code --add-modules}, - * {@code --patch-module}, {@code --add-exports}, {@code --add-opens}, and {@code --add-reads}, and also the - * {@code Add-Exports} and {@code Add-Opens} entries from jarfile manifest files encountered during scanning. + * Get the module path info provided on the commandline with + * {@code --module-path}, {@code --add-modules}, + * {@code --patch-module}, {@code --add-exports}, {@code --add-opens}, and + * {@code --add-reads}, and also the + * {@code Add-Exports} and {@code Add-Opens} entries from jarfile manifest files + * encountered during scanning. * *

- * Note that the returned {@link ModulePathInfo} object does not include classpath entries from the traditional - * classpath or system modules. Use {@link #getModules()} to get all visible modules, including anonymous, + * Note that the returned {@link ModulePathInfo} object does not include + * classpath entries from the traditional + * classpath or system modules. Use {@link #getModules()} to get all visible + * modules, including anonymous, * automatic and system modules. * * @return The {@link ModulePathInfo}. @@ -463,7 +499,8 @@ public ModulePathInfo getModulePathInfo() { /** * Get the list of all resources. * - * @return A list of all resources (including classfiles and non-classfiles) found in accepted packages. + * @return A list of all resources (including classfiles and non-classfiles) + * found in accepted packages. */ public ResourceList getAllResources() { if (allAcceptedResourcesCached == null) { @@ -479,10 +516,12 @@ public ResourceList getAllResources() { } /** - * Get a map from resource path to {@link Resource} for all resources (including classfiles and non-classfiles) + * Get a map from resource path to {@link Resource} for all resources (including + * classfiles and non-classfiles) * found in accepted packages. * - * @return The map from resource path to {@link Resource} for all resources (including classfiles and + * @return The map from resource path to {@link Resource} for all resources + * (including classfiles and * non-classfiles) found in accepted packages. */ public Map getAllResourcesAsMap() { @@ -502,13 +541,18 @@ public Map getAllResourcesAsMap() { } /** - * Get the list of all resources found in accepted packages that have the given path, relative to the package - * root of the classpath element. May match several resources, up to one per classpath element. + * Get the list of all resources found in accepted packages that have the given + * path, relative to the package + * root of the classpath element. May match several resources, up to one per + * classpath element. * * @param resourcePath - * A complete resource path, relative to the classpath entry package root. - * @return A list of all resources found in accepted packages that have the given path, relative to the package - * root of the classpath element. May match several resources, up to one per classpath element. + * A complete resource path, relative to the classpath entry + * package root. + * @return A list of all resources found in accepted packages that have the + * given path, relative to the package + * root of the classpath element. May match several resources, up to one + * per classpath element. */ public ResourceList getResourcesWithPath(final String resourcePath) { if (closed.get()) { @@ -518,10 +562,12 @@ public ResourceList getResourcesWithPath(final String resourcePath) { /* removeFinalSlash = */ true); ResourceList matchingResources = null; if (getResourcesWithPathCallCount.incrementAndGet() > 3) { - // If numerous calls are made, produce and cache a single HashMap for O(1) access time + // If numerous calls are made, produce and cache a single HashMap for O(1) + // access time matchingResources = getAllResourcesAsMap().get(path); } else { - // If just a few calls are made, directly search for resource with the requested path + // If just a few calls are made, directly search for resource with the requested + // path for (final ClasspathElement classpathElt : classpathOrder) { for (final Resource res : classpathElt.acceptedResources) { if (res.getPath().equals(path)) { @@ -537,18 +583,27 @@ public ResourceList getResourcesWithPath(final String resourcePath) { } /** - * Get the list of all resources found in any classpath element, whether in accepted packages or not (as long - * as the resource is not rejected), that have the given path, relative to the package root of the classpath - * element. May match several resources, up to one per classpath element. Note that this may not return a - * non-accepted resource, particularly when scanning directory classpath elements, because recursive scanning - * terminates once there are no possible accepted resources below a given directory. However, resources in + * Get the list of all resources found in any classpath element, whether in + * accepted packages or not (as long + * as the resource is not rejected), that have the given path, relative to + * the package root of the classpath + * element. May match several resources, up to one per classpath element. Note + * that this may not return a + * non-accepted resource, particularly when scanning directory classpath + * elements, because recursive scanning + * terminates once there are no possible accepted resources below a given + * directory. However, resources in * ancestral directories of accepted directories can be found using this method. * * @param resourcePath - * A complete resource path, relative to the classpath entry package root. - * @return A list of all resources found in any classpath element, whether in accepted packages or not (as - * long as the resource is not rejected), that have the given path, relative to the package root of - * the classpath element. May match several resources, up to one per classpath element. + * A complete resource path, relative to the classpath entry + * package root. + * @return A list of all resources found in any classpath element, whether in + * accepted packages or not (as + * long as the resource is not rejected), that have the given path, + * relative to the package root of + * the classpath element. May match several resources, up to one per + * classpath element. */ public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath) { if (closed.get()) { @@ -570,10 +625,14 @@ public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath * Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. * * @param resourcePath - * A complete resource path, relative to the classpath entry package root. - * @return A list of all resources found in any classpath element, whether in accepted packages or not (as - * long as the resource is not rejected), that have the given path, relative to the package root of - * the classpath element. May match several resources, up to one per classpath element. + * A complete resource path, relative to the classpath entry + * package root. + * @return A list of all resources found in any classpath element, whether in + * accepted packages or not (as + * long as the resource is not rejected), that have the given path, + * relative to the package root of + * the classpath element. May match several resources, up to one per + * classpath element. * @deprecated Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. */ @Deprecated @@ -582,11 +641,13 @@ public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourceP } /** - * Get the list of all resources found in accepted packages that have the requested leafname. + * Get the list of all resources found in accepted packages that have the + * requested leafname. * * @param leafName - * A resource leaf filename. - * @return A list of all resources found in accepted packages that have the requested leafname. + * A resource leaf filename. + * @return A list of all resources found in accepted packages that have the + * requested leafname. */ public ResourceList getResourcesWithLeafName(final String leafName) { if (closed.get()) { @@ -609,11 +670,14 @@ public ResourceList getResourcesWithLeafName(final String leafName) { } /** - * Get the list of all resources found in accepted packages that have the requested filename extension. + * Get the list of all resources found in accepted packages that have the + * requested filename extension. * * @param extension - * A filename extension, e.g. "xml" to match all resources ending in ".xml". - * @return A list of all resources found in accepted packages that have the requested filename extension. + * A filename extension, e.g. "xml" to match all resources + * ending in ".xml". + * @return A list of all resources found in accepted packages that have the + * requested filename extension. */ public ResourceList getResourcesWithExtension(final String extension) { if (closed.get()) { @@ -642,12 +706,14 @@ public ResourceList getResourcesWithExtension(final String extension) { } /** - * Get the list of all resources found in accepted packages that have a path matching the requested regex + * Get the list of all resources found in accepted packages that have a path + * matching the requested regex * pattern. See also {{@link #getResourcesMatchingWildcard(String)}. * * @param pattern - * A pattern to match {@link Resource} paths with. - * @return A list of all resources found in accepted packages that have a path matching the requested pattern. + * A pattern to match {@link Resource} paths with. + * @return A list of all resources found in accepted packages that have a path + * matching the requested pattern. */ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { if (closed.get()) { @@ -669,26 +735,34 @@ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { } /** - * Get the list of all resources found in accepted packages that have a path matching the requested wildcard + * Get the list of all resources found in accepted packages that have a path + * matching the requested wildcard * string. * *

* The wildcard string may contain: *

    - *
  • Single asterisks, to match zero or more of any character other than '/'
  • + *
  • Single asterisks, to match zero or more of any character other than + * '/'
  • *
  • Double asterisks, to match zero or more of any character
  • *
  • Question marks, to match one character
  • - *
  • Any other regexp-style syntax, such as character sets (denoted by square brackets) -- the remainder of - * the expression is passed through to the Java regex parser, after escaping dot characters.
  • + *
  • Any other regexp-style syntax, such as character sets (denoted by square + * brackets) -- the remainder of + * the expression is passed through to the Java regex parser, after escaping dot + * characters.
  • *
* *

- * The wildcard string is translated in a simplistic way into a regex. If you need more complex pattern - * matching, use a regex directly, via {@link #getResourcesMatchingPattern(Pattern)}. + * The wildcard string is translated in a simplistic way into a regex. If you + * need more complex pattern + * matching, use a regex directly, via + * {@link #getResourcesMatchingPattern(Pattern)}. * * @param wildcardString - * A wildcard (glob) pattern to match {@link Resource} paths with. - * @return A list of all resources found in accepted packages that have a path matching the requested wildcard + * A wildcard (glob) pattern to match {@link Resource} + * paths with. + * @return A list of all resources found in accepted packages that have a path + * matching the requested wildcard * string. */ public ResourceList getResourcesMatchingWildcard(final String wildcardString) { @@ -702,12 +776,14 @@ public ResourceList getResourcesMatchingWildcard(final String wildcardString) { // Modules /** - * Get the {@link ModuleInfo} object for the named module, or null if no module of the requested name was found + * Get the {@link ModuleInfo} object for the named module, or null if no module + * of the requested name was found * during the scan. * * @param moduleName - * The module name. - * @return The {@link ModuleInfo} object for the named module, or null if the module was not found. + * The module name. + * @return The {@link ModuleInfo} object for the named module, or null if the + * module was not found. */ public ModuleInfo getModuleInfo(final String moduleName) { if (closed.get()) { @@ -722,7 +798,8 @@ public ModuleInfo getModuleInfo(final String moduleName) { /** * Get all modules found during the scan. * - * @return A list of all modules found during the scan, or the empty list if none. + * @return A list of all modules found during the scan, or the empty list if + * none. */ public ModuleInfoList getModuleInfo() { if (closed.get()) { @@ -738,12 +815,14 @@ public ModuleInfoList getModuleInfo() { // Packages /** - * Get the {@link PackageInfo} object for the named package, or null if no package of the requested name was + * Get the {@link PackageInfo} object for the named package, or null if no + * package of the requested name was * found during the scan. * * @param packageName - * The package name. - * @return The {@link PackageInfo} object for the named package, or null if the package was not found. + * The package name. + * @return The {@link PackageInfo} object for the named package, or null if the + * package was not found. */ public PackageInfo getPackageInfo(final String packageName) { if (closed.get()) { @@ -758,7 +837,8 @@ public PackageInfo getPackageInfo(final String packageName) { /** * Get all packages found during the scan. * - * @return A list of all packages found during the scan, or the empty list if none. + * @return A list of all packages found during the scan, or the empty list if + * none. */ public PackageInfoList getPackageInfo() { if (closed.get()) { @@ -774,16 +854,24 @@ public PackageInfoList getPackageInfo() { // Class dependencies /** - * Get a map from the {@link ClassInfo} object for each accepted class to a list of the classes referenced by - * that class (i.e. returns a map from dependents to dependencies). Note that you need to call - * {@link ClassGraph#enableInterClassDependencies()} before {@link ClassGraph#scan()} for this method to work. - * You should also call {@link ClassGraph#enableExternalClasses()} before {@link ClassGraph#scan()} if you want - * non-accepted classes to appear in the result. See also {@link #getReverseClassDependencyMap()}, which inverts + * Get a map from the {@link ClassInfo} object for each accepted class to a list + * of the classes referenced by + * that class (i.e. returns a map from dependents to dependencies). Note that + * you need to call + * {@link ClassGraph#enableInterClassDependencies()} before + * {@link ClassGraph#scan()} for this method to work. + * You should also call {@link ClassGraph#enableExternalClasses()} before + * {@link ClassGraph#scan()} if you want + * non-accepted classes to appear in the result. See also + * {@link #getReverseClassDependencyMap()}, which inverts * the map. * - * @return A map from a {@link ClassInfo} object for each accepted class to a list of the classes referenced by - * that class (i.e. returns a map from dependents to dependencies). Each map value is the result of - * calling {@link ClassInfo#getClassDependencies()} on the corresponding key. + * @return A map from a {@link ClassInfo} object for each accepted class to a + * list of the classes referenced by + * that class (i.e. returns a map from dependents to dependencies). Each + * map value is the result of + * calling {@link ClassInfo#getClassDependencies()} on the corresponding + * key. */ public Map getClassDependencyMap() { final Map map = new HashMap<>(); @@ -794,15 +882,22 @@ public Map getClassDependencyMap() { } /** - * Get the reverse class dependency map, i.e. a map from the {@link ClassInfo} object for each dependency class - * (accepted or not) to a list of the accepted classes that referenced that class as a dependency (i.e. returns + * Get the reverse class dependency map, i.e. a map from the {@link ClassInfo} + * object for each dependency class + * (accepted or not) to a list of the accepted classes that referenced that + * class as a dependency (i.e. returns * a map from dependencies to dependents). Note that you need to call - * {@link ClassGraph#enableInterClassDependencies()} before {@link ClassGraph#scan()} for this method to work. - * You should also call {@link ClassGraph#enableExternalClasses()} before {@link ClassGraph#scan()} if you want - * non-accepted classes to appear in the result. See also {@link #getClassDependencyMap}. + * {@link ClassGraph#enableInterClassDependencies()} before + * {@link ClassGraph#scan()} for this method to work. + * You should also call {@link ClassGraph#enableExternalClasses()} before + * {@link ClassGraph#scan()} if you want + * non-accepted classes to appear in the result. See also + * {@link #getClassDependencyMap}. * - * @return A map from a {@link ClassInfo} object for each dependency class (accepted or not) to a list of the - * accepted classes that referenced that class as a dependency (i.e. returns a map from dependencies to + * @return A map from a {@link ClassInfo} object for each dependency class + * (accepted or not) to a list of the + * accepted classes that referenced that class as a dependency (i.e. + * returns a map from dependencies to * dependents). */ public Map getReverseClassDependencyMap() { @@ -827,12 +922,14 @@ public Map getReverseClassDependencyMap() { // Classes /** - * Get the {@link ClassInfo} object for the named class, or null if no class of the requested name was found in + * Get the {@link ClassInfo} object for the named class, or null if no class of + * the requested name was found in * an accepted/non-rejected package during the scan. * * @param className - * The class name. - * @return The {@link ClassInfo} object for the named class, or null if the class was not found. + * The class name. + * @return The {@link ClassInfo} object for the named class, or null if the + * class was not found. */ public ClassInfo getClassInfo(final String className) { if (closed.get()) { @@ -847,7 +944,8 @@ public ClassInfo getClassInfo(final String className) { /** * Get all classes, interfaces and annotations found during the scan. * - * @return A list of all accepted classes found during the scan, or the empty list if none. + * @return A list of all accepted classes found during the scan, or the empty + * list if none. */ public ClassInfoList getAllClasses() { if (closed.get()) { @@ -862,7 +960,8 @@ public ClassInfoList getAllClasses() { /** * Get all {@link Enum} classes found during the scan. * - * @return A list of all {@link Enum} classes found during the scan, or the empty list if none. + * @return A list of all {@link Enum} classes found during the scan, or the + * empty list if none. */ public ClassInfoList getAllEnums() { if (closed.get()) { @@ -877,7 +976,8 @@ public ClassInfoList getAllEnums() { /** * Get all {@code record} classes found during the scan (JDK 14+). * - * @return A list of all {@code record} classes found during the scan, or the empty list if none. + * @return A list of all {@code record} classes found during the scan, or the + * empty list if none. */ public ClassInfoList getAllRecords() { if (closed.get()) { @@ -890,10 +990,12 @@ public ClassInfoList getAllRecords() { } /** - * Get a map from class name to {@link ClassInfo} object for all classes, interfaces and annotations found + * Get a map from class name to {@link ClassInfo} object for all classes, + * interfaces and annotations found * during the scan. * - * @return The map from class name to {@link ClassInfo} object for all classes, interfaces and annotations found + * @return The map from class name to {@link ClassInfo} object for all classes, + * interfaces and annotations found * during the scan. */ public Map getAllClassesAsMap() { @@ -907,9 +1009,11 @@ public Map getAllClassesAsMap() { } /** - * Get all standard (non-interface/non-annotation) classes found during the scan. + * Get all standard (non-interface/non-annotation) classes found during the + * scan. * - * @return A list of all accepted standard classes found during the scan, or the empty list if none. + * @return A list of all accepted standard classes found during the scan, or the + * empty list if none. */ public ClassInfoList getAllStandardClasses() { if (closed.get()) { @@ -925,7 +1029,7 @@ public ClassInfoList getAllStandardClasses() { * Get all subclasses of the superclass. * * @param superclass - * The superclass. + * The superclass. * @return A list of subclasses of the superclass, or the empty list if none. */ public ClassInfoList getSubclasses(final Class superclass) { @@ -936,8 +1040,9 @@ public ClassInfoList getSubclasses(final Class superclass) { * Get all subclasses of the named superclass. * * @param superclassName - * The name of the superclass. - * @return A list of subclasses of the named superclass, or the empty list if none. + * The name of the superclass. + * @return A list of subclasses of the named superclass, or the empty list if + * none. */ public ClassInfoList getSubclasses(final String superclassName) { if (closed.get()) { @@ -959,8 +1064,9 @@ public ClassInfoList getSubclasses(final String superclassName) { * Get superclasses of the named subclass. * * @param subclassName - * The name of the subclass. - * @return A list of superclasses of the named subclass, or the empty list if none. + * The name of the subclass. + * @return A list of superclasses of the named subclass, or the empty list if + * none. */ public ClassInfoList getSuperclasses(final String subclassName) { if (closed.get()) { @@ -977,8 +1083,9 @@ public ClassInfoList getSuperclasses(final String subclassName) { * Get superclasses of the subclass. * * @param subclass - * The subclass. - * @return A list of superclasses of the named subclass, or the empty list if none. + * The subclass. + * @return A list of superclasses of the named subclass, or the empty list if + * none. */ public ClassInfoList getSuperclasses(final Class subclass) { return getSuperclasses(subclass.getName()); @@ -988,8 +1095,9 @@ public ClassInfoList getSuperclasses(final Class subclass) { * Get classes that have a method with an annotation of the named type. * * @param methodAnnotation - * the method annotation. - * @return A list of classes with a method that has an annotation of the named type, or the empty list if none. + * the method annotation. + * @return A list of classes with a method that has an annotation of the named + * type, or the empty list if none. */ public ClassInfoList getClassesWithMethodAnnotation(final Class methodAnnotation) { Assert.isAnnotation(methodAnnotation); @@ -1000,8 +1108,9 @@ public ClassInfoList getClassesWithMethodAnnotation(final Class fieldAnnotation) { Assert.isAnnotation(fieldAnnotation); @@ -1065,8 +1180,9 @@ public ClassInfoList getClassesWithFieldAnnotation(final Class classRef) { @@ -1133,12 +1257,14 @@ public ClassInfoList getInterfaces(final Class classRef) { } /** - * Get all classes that implement (or have superclasses that implement) the interface (or one of its + * Get all classes that implement (or have superclasses that implement) the + * interface (or one of its * subinterfaces). * * @param interfaceClass - * The interface class. - * @return A list of all classes that implement the interface, or the empty list if none. + * The interface class. + * @return A list of all classes that implement the interface, or the empty list + * if none. */ public ClassInfoList getClassesImplementing(final Class interfaceClass) { Assert.isInterface(interfaceClass); @@ -1146,12 +1272,14 @@ public ClassInfoList getClassesImplementing(final Class interfaceClass) { } /** - * Get all classes that implement (or have superclasses that implement) the named interface (or one of its + * Get all classes that implement (or have superclasses that implement) the + * named interface (or one of its * subinterfaces). * * @param interfaceName - * The interface name. - * @return A list of all classes that implement the named interface, or the empty list if none. + * The interface name. + * @return A list of all classes that implement the named interface, or the + * empty list if none. */ public ClassInfoList getClassesImplementing(final String interfaceName) { if (closed.get()) { @@ -1168,9 +1296,11 @@ public ClassInfoList getClassesImplementing(final String interfaceName) { // Annotations /** - * Get all annotation classes found during the scan. See also {@link #getAllInterfacesAndAnnotations()}. + * Get all annotation classes found during the scan. See also + * {@link #getAllInterfacesAndAnnotations()}. * - * @return A list of all annotation classes found during the scan, or the empty list if none. + * @return A list of all annotation classes found during the scan, or the empty + * list if none. */ public ClassInfoList getAllAnnotations() { if (closed.get()) { @@ -1184,10 +1314,12 @@ public ClassInfoList getAllAnnotations() { } /** - * Get all interface or annotation classes found during the scan. (Annotations are technically interfaces, and + * Get all interface or annotation classes found during the scan. (Annotations + * are technically interfaces, and * they can be implemented.) * - * @return A list of all accepted interfaces found during the scan, or the empty list if none. + * @return A list of all accepted interfaces found during the scan, or the empty + * list if none. */ public ClassInfoList getAllInterfacesAndAnnotations() { if (closed.get()) { @@ -1204,8 +1336,9 @@ public ClassInfoList getAllInterfacesAndAnnotations() { * Get classes with the class annotation or meta-annotation. * * @param annotation - * The class annotation or meta-annotation. - * @return A list of all non-annotation classes that were found with the class annotation during the scan, or + * The class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the class + * annotation during the scan, or * the empty list if none. */ public ClassInfoList getClassesWithAnnotation(final Class annotation) { @@ -1217,8 +1350,9 @@ public ClassInfoList getClassesWithAnnotation(final Class * Get classes with the named class annotation or meta-annotation. * * @param annotationName - * The name of the class annotation or meta-annotation. - * @return A list of all non-annotation classes that were found with the named class annotation during the scan, + * The name of the class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the named + * class annotation during the scan, * or the empty list if none. */ public ClassInfoList getClassesWithAnnotation(final String annotationName) { @@ -1234,14 +1368,18 @@ public ClassInfoList getClassesWithAnnotation(final String annotationName) { } /** - * Get annotations on the named class. This only returns the annotating classes; to read annotation parameters, - * call {@link #getClassInfo(String)} to get the {@link ClassInfo} object for the named class, then if the - * {@link ClassInfo} object is non-null, call {@link ClassInfo#getAnnotationInfo()} to get detailed annotation + * Get annotations on the named class. This only returns the annotating classes; + * to read annotation parameters, + * call {@link #getClassInfo(String)} to get the {@link ClassInfo} object for + * the named class, then if the + * {@link ClassInfo} object is non-null, call + * {@link ClassInfo#getAnnotationInfo()} to get detailed annotation * info. * * @param className - * The name of the class. - * @return A list of all annotation classes that were found with the named class annotation during the scan, or + * The name of the class. + * @return A list of all annotation classes that were found with the named class + * annotation during the scan, or * the empty list if none. */ public ClassInfoList getAnnotationsOnClass(final String className) { @@ -1260,12 +1398,16 @@ public ClassInfoList getAnnotationsOnClass(final String className) { // Classpath modification tests /** - * Determine whether the classpath contents have been modified since the last scan. Checks the timestamps of - * files and jarfiles encountered during the previous scan to see if they have changed. Does not perform a full - * scan, so cannot detect the addition of directories that newly match accept criteria -- you need to perform a + * Determine whether the classpath contents have been modified since the last + * scan. Checks the timestamps of + * files and jarfiles encountered during the previous scan to see if they have + * changed. Does not perform a full + * scan, so cannot detect the addition of directories that newly match accept + * criteria -- you need to perform a * full scan to detect those changes. * - * @return true if the classpath contents have been modified since the last scan. + * @return true if the classpath contents have been modified since the last + * scan. */ public boolean classpathContentsModifiedSinceScan() { if (closed.get()) { @@ -1284,16 +1426,21 @@ public boolean classpathContentsModifiedSinceScan() { } /** - * Find the maximum last-modified timestamp of any accepted file/directory/jarfile encountered during the scan. - * Checks the current timestamps, so this should increase between calls if something changes in accepted paths. - * Assumes both file and system timestamps were generated from clocks whose time was accurate. Ignores + * Find the maximum last-modified timestamp of any accepted + * file/directory/jarfile encountered during the scan. + * Checks the current timestamps, so this should increase between calls if + * something changes in accepted paths. + * Assumes both file and system timestamps were generated from clocks whose time + * was accurate. Ignores * timestamps greater than the system time. * *

- * This method cannot in general tell if classpath has changed (or modules have been added or removed) if it is + * This method cannot in general tell if classpath has changed (or modules have + * been added or removed) if it is * run twice during the same runtime session. * - * @return the maximum last-modified time for accepted files/directories/jars encountered during the scan. + * @return the maximum last-modified time for accepted files/directories/jars + * encountered during the scan. */ public long classpathContentsLastModifiedTime() { if (closed.get()) { @@ -1315,7 +1462,8 @@ public long classpathContentsLastModifiedTime() { // Classloading /** - * Get the ClassLoader order, respecting parent-first/parent-last delegation order. + * Get the ClassLoader order, respecting parent-first/parent-last delegation + * order. * * @return the class loader order. */ @@ -1324,26 +1472,40 @@ ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { } /** - * Load a class given a class name. If ignoreExceptions is false, and the class cannot be loaded (due to - * classloading error, or due to an exception being thrown in the class initialization block), an - * IllegalArgumentException is thrown; otherwise, the class will simply be skipped if an exception is thrown. + * Load a class given a class name. If ignoreExceptions is false, and the class + * cannot be loaded (due to + * classloading error, or due to an exception being thrown in the class + * initialization block), an + * IllegalArgumentException is thrown; otherwise, the class will simply be + * skipped if an exception is thrown. * *

- * Enable verbose scanning to see details of any exceptions thrown during classloading, even if ignoreExceptions + * Enable verbose scanning to see details of any exceptions thrown during + * classloading, even if ignoreExceptions * is false. * * @param className - * the class to load. + * the class to load. * @param returnNullIfClassNotFound - * If true, null is returned if there was an exception during classloading, otherwise - * IllegalArgumentException is thrown if a class could not be loaded. - * @return a reference to the loaded class, or null if the class could not be loaded and ignoreExceptions is + * If true, null is returned if there was an + * exception during classloading, otherwise + * IllegalArgumentException is thrown if a + * class could not be loaded. + * @return a reference to the loaded class, or null if the class could not be + * loaded and ignoreExceptions is * true. * @throws IllegalArgumentException - * if ignoreExceptions is false, IllegalArgumentException is thrown if there were problems loading - * or initializing the class. (Note that class initialization on load is disabled by default, you - * can enable it with {@code ClassGraph#initializeLoadedClasses(true)} .) Otherwise exceptions are - * suppressed, and null is returned if any of these problems occurs. + * if ignoreExceptions is false, + * IllegalArgumentException is thrown if there + * were problems loading + * or initializing the class. (Note that class + * initialization on load is disabled by + * default, you + * can enable it with + * {@code ClassGraph#initializeLoadedClasses(true)} + * .) Otherwise exceptions are + * suppressed, and null is returned if any of + * these problems occurs. */ public Class loadClass(final String className, final boolean returnNullIfClassNotFound) throws IllegalArgumentException { @@ -1365,31 +1527,45 @@ public Class loadClass(final String className, final boolean returnNullIfClas } /** - * Load a class given a class name. If ignoreExceptions is false, and the class cannot be loaded (due to - * classloading error, or due to an exception being thrown in the class initialization block), an - * IllegalArgumentException is thrown; otherwise, the class will simply be skipped if an exception is thrown. + * Load a class given a class name. If ignoreExceptions is false, and the class + * cannot be loaded (due to + * classloading error, or due to an exception being thrown in the class + * initialization block), an + * IllegalArgumentException is thrown; otherwise, the class will simply be + * skipped if an exception is thrown. * *

- * Enable verbose scanning to see details of any exceptions thrown during classloading, even if ignoreExceptions + * Enable verbose scanning to see details of any exceptions thrown during + * classloading, even if ignoreExceptions * is false. * * @param - * the superclass or interface type. + * the superclass or interface type. * @param className - * the class to load. + * the class to load. * @param superclassOrInterfaceType - * The class type to cast the result to. + * The class type to cast the result to. * @param returnNullIfClassNotFound - * If true, null is returned if there was an exception during classloading, otherwise - * IllegalArgumentException is thrown if a class could not be loaded. - * @return a reference to the loaded class, or null if the class could not be loaded and ignoreExceptions is + * If true, null is returned if there was an + * exception during classloading, otherwise + * IllegalArgumentException is thrown if a + * class could not be loaded. + * @return a reference to the loaded class, or null if the class could not be + * loaded and ignoreExceptions is * true. * @throws IllegalArgumentException - * if ignoreExceptions is false, IllegalArgumentException is thrown if there were problems loading - * the class, initializing the class, or casting it to the requested type. (Note that class - * initialization on load is disabled by default, you can enable it with - * {@code ClassGraph#initializeLoadedClasses(true)} .) Otherwise exceptions are suppressed, and null - * is returned if any of these problems occurs. + * if ignoreExceptions is false, + * IllegalArgumentException is thrown if there + * were problems loading + * the class, initializing the class, or + * casting it to the requested type. (Note that + * class + * initialization on load is disabled by + * default, you can enable it with + * {@code ClassGraph#initializeLoadedClasses(true)} + * .) Otherwise exceptions are suppressed, and + * null + * is returned if any of these problems occurs. */ public Class loadClass(final String className, final Class superclassOrInterfaceType, final boolean returnNullIfClassNotFound) throws IllegalArgumentException { @@ -1433,7 +1609,7 @@ public Class loadClass(final String className, final Class superclassO * Deserialize a ScanResult from previously-serialized JSON. * * @param json - * The JSON string for the serialized {@link ScanResult}. + * The JSON string for the serialized {@link ScanResult}. * @return The deserialized {@link ScanResult}. */ @SuppressWarnings("null") @@ -1453,12 +1629,15 @@ public static ScanResult fromJSON(final String json) { final SerializationFormat deserialized = JSONDeserializer.deserializeObject(SerializationFormat.class, json); if (deserialized == null || !deserialized.format.equals(CURRENT_SERIALIZATION_FORMAT)) { - // Probably the deserialization failed before now anyway, if fields have changed, etc. + // Probably the deserialization failed before now anyway, if fields have + // changed, etc. throw new IllegalArgumentException("JSON was serialized by newer version of ClassGraph"); } - // Perform a new "scan" with performScan set to false, which resolves all the ClasspathElement objects - // and scans classpath element paths (needed for classloading), but does not scan the actual classfiles + // Perform a new "scan" with performScan set to false, which resolves all the + // ClasspathElement objects + // and scans classpath element paths (needed for classloading), but does not + // scan the actual classfiles final ClassGraph classGraph = new ClassGraph(); classGraph.scanSpec = deserialized.scanSpec; final ScanResult scanResult; @@ -1468,7 +1647,8 @@ public static ScanResult fromJSON(final String json) { } scanResult.rawClasspathEltOrderStrs = deserialized.classpath; - // Set the fields related to ClassInfo in the new ScanResult, based on the deserialized JSON + // Set the fields related to ClassInfo in the new ScanResult, based on the + // deserialized JSON scanResult.scanSpec = deserialized.scanSpec; scanResult.classNameToClassInfo = new HashMap<>(); if (deserialized.classInfo != null) { @@ -1490,7 +1670,7 @@ public static ScanResult fromJSON(final String json) { } } - // Index Resource and ClassInfo objects + // Index Resource and ClassInfo objects scanResult.indexResourcesAndClassInfo(/* log = */ null); scanResult.isObtainedFromDeserialization = true; @@ -1501,7 +1681,8 @@ public static ScanResult fromJSON(final String json) { * Serialize a ScanResult to JSON. * * @param indentWidth - * If greater than 0, JSON will be formatted (indented), otherwise it will be minified (un-indented). + * If greater than 0, JSON will be formatted (indented), + * otherwise it will be minified (un-indented). * @return This {@link ScanResult}, serialized as a JSON string. */ public String toJSON(final int indentWidth) { @@ -1531,10 +1712,12 @@ public String toJSON() { } /** - * Checks if this {@link ScanResult} was obtained from JSON by deserialization, by calling + * Checks if this {@link ScanResult} was obtained from JSON by deserialization, + * by calling * {@link #fromJSON(String)}. * - * @return True if this {@link ScanResult} was obtained from JSON by deserialization. + * @return True if this {@link ScanResult} was obtained from JSON by + * deserialization. */ public boolean isObtainedFromDeserialization() { return isObtainedFromDeserialization; @@ -1543,9 +1726,12 @@ public boolean isObtainedFromDeserialization() { // ------------------------------------------------------------------------------------------------------------- /** - * Free any temporary files created by extracting jars or files from within jars. Without calling this method, - * the temporary files created by extracting the inner jars will be removed in a finalizer, called by the - * garbage collector (or at JVM shutdown). If you don't want to experience long GC pauses, make sure you call + * Free any temporary files created by extracting jars or files from within + * jars. Without calling this method, + * the temporary files created by extracting the inner jars will be removed in a + * finalizer, called by the + * garbage collector (or at JVM shutdown). If you don't want to experience long + * GC pauses, make sure you call * this close method when you have finished with the {@link ScanResult}. */ @Override @@ -1569,10 +1755,12 @@ public void close() { } classGraphClassLoader = null; if (classNameToClassInfo != null) { - // Don't clear classNameToClassInfo, since it may be used by ClassGraphClassLoader (#399). - // Just rely on the garbage collector to collect these once the ScanResult goes out of scope. - // classNameToClassInfo.clear(); - // classNameToClassInfo = null; + // Don't clear classNameToClassInfo, since it may be used by + // ClassGraphClassLoader (#399). + // Just rely on the garbage collector to collect these once the ScanResult goes + // out of scope. + // classNameToClassInfo.clear(); + // classNameToClassInfo = null; } if (packageNameToPackageInfo != null) { packageNameToPackageInfo.clear(); @@ -1586,8 +1774,10 @@ public void close() { fileToLastModified.clear(); fileToLastModified = null; } - // nestedJarHandler should be closed last, since it needs to have all MappedByteBuffer refs - // dropped before it tries to delete any temporary files that were written to disk + // nestedJarHandler should be closed last, since it needs to have all + // MappedByteBuffer refs + // dropped before it tries to delete any temporary files that were written to + // disk if (nestedJarHandler != null) { nestedJarHandler.close(topLevelLog); nestedJarHandler = null; @@ -1595,7 +1785,8 @@ public void close() { classGraphClassLoader = null; classpathFinder = null; reflectionUtils = null; - // Flush log on exit, in case additional log entries were generated after scan() completed + // Flush log on exit, in case additional log entries were generated after scan() + // completed if (topLevelLog != null) { topLevelLog.flush(); } @@ -1603,11 +1794,16 @@ public void close() { } /** - * Close all {@link ScanResult} instances that have not yet been closed. Note that this will close all open - * {@link ScanResult} instances for any class that uses the classloader that the {@link ScanResult} class is - * cached in -- so if you call this method, you need to ensure that the lifecycle of the classloader matches the - * lifecycle of your application, or that two concurrent applications don't share the same classloader, - * otherwise one application might close another application's {@link ScanResult} instances while they are still + * Close all {@link ScanResult} instances that have not yet been closed. Note + * that this will close all open + * {@link ScanResult} instances for any class that uses the classloader that the + * {@link ScanResult} class is + * cached in -- so if you call this method, you need to ensure that the + * lifecycle of the classloader matches the + * lifecycle of your application, or that two concurrent applications don't + * share the same classloader, + * otherwise one application might close another application's + * {@link ScanResult} instances while they are still * in use. */ public static void closeAll() { diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java index 0098073d6..b421332b1 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -28,7 +28,6 @@ */ package nonapi.io.github.classgraph.fileslice; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; @@ -45,7 +44,7 @@ import nonapi.io.github.classgraph.utils.FileUtils; /** A {@link Path} slice. */ -public class PathSlice extends Slice implements Closeable { +public class PathSlice extends Slice { /** The {@link Path}. */ public final Path path; @@ -65,18 +64,19 @@ public class PathSlice extends Slice implements Closeable { * Constructor for treating a range of a file as a slice. * * @param parentSlice - * the parent slice + * the parent slice * @param offset - * the offset of the sub-slice within the parent slice + * the offset of the sub-slice within the parent slice * @param length - * the length of the sub-slice + * the length of the sub-slice * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or + * -1 if unknown, or 0 of this is not a deflated + * zip entry. * @param nestedJarHandler - * the nested jar handler + * the nested jar handler */ private PathSlice(final PathSlice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, @@ -88,24 +88,27 @@ private PathSlice(final PathSlice parentSlice, final long offset, final long len this.fileLength = parentSlice.fileLength; this.isTopLevelFileSlice = false; - // Only mark toplevel file slices as open (sub slices don't need to be marked as open since - // they don't need to be closed, they just copy the resource references of the toplevel slice) + // Only mark toplevel file slices as open (sub slices don't need to be marked as + // open since + // they don't need to be closed, they just copy the resource references of the + // toplevel slice) } /** * Constructor for toplevel file slice. * * @param path - * the path + * the path * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or + * -1 if unknown, or 0 of this is not a deflated + * zip entry. * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @throws IOException - * if the file cannot be opened. + * if the file cannot be opened. */ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) throws IOException { @@ -119,7 +122,8 @@ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long i this.fileLength = fileChannel.size(); this.isTopLevelFileSlice = true; - // Had to use 0L for sliceLength in call to super, since FileChannel wasn't open yet => update sliceLength + // Had to use 0L for sliceLength in call to super, since FileChannel wasn't open + // yet => update sliceLength this.sliceLength = fileLength; // Mark toplevel slice as open @@ -130,11 +134,11 @@ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long i * Constructor for toplevel file slice. * * @param path - * the path + * the path * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @throws IOException - * if the file cannot be opened. + * if the file cannot be opened. */ public PathSlice(final Path path, final NestedJarHandler nestedJarHandler) throws IOException { this(path, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); @@ -144,14 +148,15 @@ public PathSlice(final Path path, final NestedJarHandler nestedJarHandler) throw * Slice the file. * * @param offset - * the offset of the sub-slice within the parent slice + * the offset of the sub-slice within the parent slice * @param length - * the length of the sub-slice + * the length of the sub-slice * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or + * -1 if unknown, or 0 of this is not a deflated + * zip entry. * @return the slice */ @Override @@ -179,7 +184,7 @@ public RandomAccessReader randomAccessReader() { * * @return the byte[] * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ @Override public byte[] load() throws IOException { @@ -207,18 +212,21 @@ public byte[] load() throws IOException { } /** - * Read the slice into a {@link ByteBuffer} (or memory-map the slice to a {@link MappedByteBuffer}, if + * Read the slice into a {@link ByteBuffer} (or memory-map the slice to a + * {@link MappedByteBuffer}, if * {@link ClassGraph#enableMemoryMapping()} was called.) * * @return the byte buffer * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ @Override public ByteBuffer read() throws IOException { if (isDeflatedZipEntry) { - // Inflate to RAM if deflated (unfortunately there is no lazy-loading ByteBuffer that will - // decompress partial streams on demand, so we have to decompress the whole zip entry) + // Inflate to RAM if deflated (unfortunately there is no lazy-loading ByteBuffer + // that will + // decompress partial streams on demand, so we have to decompress the whole zip + // entry) if (inflatedLengthHint > FileUtils.MAX_BUFFER_SIZE) { throw new IOException("Uncompressed size is larger than 2GB"); } @@ -246,7 +254,8 @@ public int hashCode() { public void close() { if (!isClosed.getAndSet(true)) { if (isTopLevelFileSlice && fileChannel != null) { - // Only close the FileChannel in the toplevel file slice, so that it is only closed once + // Only close the FileChannel in the toplevel file slice, so that it is only + // closed once try { // Closing raf will also close the associated FileChannel fileChannel.close(); From f4419d410172d137a020c58c18ff6e1b756d1e82 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 Nov 2023 09:55:49 -0700 Subject: [PATCH 582/713] [maven-release-plugin] prepare release classgraph-4.8.165 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 2fc4c76a6..041c67108 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.165-SNAPSHOT + 4.8.165 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -32,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.164 + classgraph-4.8.165 From f2f1a94056b7efcf39d27082933474342348667f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 Nov 2023 09:55:51 -0700 Subject: [PATCH 583/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 041c67108..58485d04e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.165 + 4.8.166-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.165 + classgraph-4.8.164 From 8b9711173d6c3b5f577c7960216bd316cfedfefa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 03:26:21 +0000 Subject: [PATCH 584/713] Bump actions/setup-java from 3 to 4 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/codeql.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d29e376b7..4d426dbec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 24bf419db..0c8702f36 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java SDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 8 From 2a122bfb82c75005568f90be8d70ec9aee89f363 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 02:27:30 +0000 Subject: [PATCH 585/713] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.5.0 to 3.6.3 Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.5.0 to 3.6.3. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.5.0...maven-javadoc-plugin-3.6.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 58485d04e..1849b2182 100644 --- a/pom.xml +++ b/pom.xml @@ -210,7 +210,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.6.3 org.apache.maven.plugins From 113108740fe2b096ffaa8b75d8a15075a39cc644 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 03:49:22 +0000 Subject: [PATCH 586/713] Bump github/codeql-action from 2 to 3 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 24bf419db..f5fcc3e56 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,15 +33,15 @@ jobs: java-version: 8 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" From 7dacb487080a964b21963fd31dfbb442ab5bc4b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 02:57:32 +0000 Subject: [PATCH 587/713] Bump org.slf4j:slf4j-api from 2.0.6 to 2.0.11 Bumps org.slf4j:slf4j-api from 2.0.6 to 2.0.11. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 58485d04e..da5aa3d14 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.6 + 2.0.11 test From 9ed7087559f2acf96285c0cd70bd37100c17bc2d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 16 Feb 2024 12:45:30 -0700 Subject: [PATCH 588/713] Sort "Opening classpath element" log entries in order of path --- src/main/java/io/github/classgraph/Scanner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 5b99befe1..36411dec6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -583,7 +583,8 @@ public ClasspathElement newInstance() throws IOException, InterruptedException { // Run open() on the ClasspathElement final LogNode subLog = log == null ? null - : log.log("Opening classpath element " + classpathElement); + : log.log(classpathElement.getURI().toString(), + "Opening classpath element " + classpathElement); // Check if the classpath element is valid (classpathElt.skipClasspathElement // will be set if not). In case of ClasspathElementZip, open or extract nested From e66ee3c902bcc60397d6ec07765726bf6109aa30 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Feb 2024 02:41:56 -0700 Subject: [PATCH 589/713] Add getClassesWithAnnotations --- pom.xml | 2 +- .../java/io/github/classgraph/ScanResult.java | 623 +++++++----------- 2 files changed, 247 insertions(+), 378 deletions(-) diff --git a/pom.xml b/pom.xml index 58485d04e..baf3fd083 100644 --- a/pom.xml +++ b/pom.xml @@ -554,7 +554,7 @@ true ossrh - https://oss.sonatype.org/ + https://s01.oss.sonatype.org/ true 10 diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index f115d42b9..365704905 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -66,8 +66,7 @@ import nonapi.io.github.classgraph.utils.LogNode; /** - * The result of a scan. You should assign a ScanResult in a try-with-resources - * block, or manually close it when you + * The result of a scan. You should assign a ScanResult in a try-with-resources block, or manually close it when you * have finished with the result of a scan. */ public final class ScanResult implements Closeable { @@ -75,8 +74,7 @@ public final class ScanResult implements Closeable { private List rawClasspathEltOrderStrs; /** - * The order of classpath elements, after inner jars have been extracted to - * temporary files, etc. + * The order of classpath elements, after inner jars have been extracted to temporary files, etc. */ private List classpathOrder; @@ -89,8 +87,7 @@ public final class ScanResult implements Closeable { private final AtomicInteger getResourcesWithPathCallCount = new AtomicInteger(); /** - * The map from path (relative to package root) to a list of {@link Resource} - * elements with the matching path. + * The map from path (relative to package root) to a list of {@link Resource} elements with the matching path. */ private Map pathToAcceptedResourcesCached; @@ -104,18 +101,14 @@ public final class ScanResult implements Closeable { private Map moduleNameToModuleInfo; /** - * The file, directory and jarfile resources timestamped during a scan, along - * with their timestamp at the time - * of the scan. For jarfiles, the timestamp represents the timestamp of all - * files within the jar. May be null, - * if this ScanResult object is the result of a call to - * ClassGraph#getUniqueClasspathElementsAsync(). + * The file, directory and jarfile resources timestamped during a scan, along with their timestamp at the time + * of the scan. For jarfiles, the timestamp represents the timestamp of all files within the jar. May be null, + * if this ScanResult object is the result of a call to ClassGraph#getUniqueClasspathElementsAsync(). */ private Map fileToLastModified; /** - * If true, this {@link ScanResult} was produced by - * {@link ScanResult#fromJSON(String)}. + * If true, this {@link ScanResult} was produced by {@link ScanResult#fromJSON(String)}. */ private boolean isObtainedFromDeserialization; @@ -145,8 +138,7 @@ public final class ScanResult implements Closeable { private final WeakReference weakReference; /** - * The set of WeakReferences to non-closed ScanResult objects. Uses - * WeakReferences so that garbage collection is + * The set of WeakReferences to non-closed ScanResult objects. Uses WeakReferences so that garbage collection is * not blocked. (Bug #233) */ private static Set> nonClosedWeakReferences = Collections @@ -161,8 +153,7 @@ public final class ScanResult implements Closeable { private static final String CURRENT_SERIALIZATION_FORMAT = "10"; /** - * A class to hold a serialized ScanResult along with the ScanSpec that was used - * to scan. + * A class to hold a serialized ScanResult along with the ScanSpec that was used to scan. */ private static class SerializationFormat { /** The serialization format. */ @@ -195,17 +186,17 @@ public SerializationFormat() { * Constructor. * * @param serializationFormatStr - * the serialization format string + * the serialization format string * @param scanSpec - * the scan spec + * the scan spec * @param classInfo - * the list of all {@link ClassInfo} objects + * the list of all {@link ClassInfo} objects * @param packageInfo - * the list of all {@link PackageInfo} objects + * the list of all {@link PackageInfo} objects * @param moduleInfo - * the list of all {@link ModuleInfo} objects + * the list of all {@link ModuleInfo} objects * @param classpath - * the classpath as a list of URL strings + * the classpath as a list of URL strings */ public SerializationFormat(final String serializationFormatStr, final ScanSpec scanSpec, final List classInfo, final List packageInfo, @@ -223,8 +214,7 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // Shutdown hook init code /** - * Static initialization (warm up classloading), called when the ClassGraph - * class is initialized. + * Static initialization (warm up classloading), called when the ClassGraph class is initialized. */ static void init(final ReflectionUtils reflectionUtils) { if (!initialized.getAndSet(true)) { @@ -248,29 +238,28 @@ static void init(final ReflectionUtils reflectionUtils) { // Constructor /** - * The result of a scan. Make sure you call complete() after calling the - * constructor. + * The result of a scan. Make sure you call complete() after calling the constructor. * * @param scanSpec - * the scan spec + * the scan spec * @param classpathOrder - * the classpath order + * the classpath order * @param rawClasspathEltOrderStrs - * the raw classpath element order + * the raw classpath element order * @param classpathFinder - * the {@link ClasspathFinder} + * the {@link ClasspathFinder} * @param classNameToClassInfo - * a map from class name to class info + * a map from class name to class info * @param packageNameToPackageInfo - * a map from package name to package info + * a map from package name to package info * @param moduleNameToModuleInfo - * a map from module name to module info + * a map from module name to module info * @param fileToLastModified - * a map from file to last modified time + * a map from file to last modified time * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @param topLevelLog - * the toplevel log + * the toplevel log */ ScanResult(final ScanSpec scanSpec, final List classpathOrder, final List rawClasspathEltOrderStrs, final ClasspathFinder classpathFinder, @@ -369,8 +358,7 @@ private void indexResourcesAndClassInfo(final LogNode log) { // Classpath / module path /** - * Returns the list of File objects for unique classpath elements (directories - * or jarfiles), in classloader + * Returns the list of File objects for unique classpath elements (directories or jarfiles), in classloader * resolution order. * * @return The unique classpath elements. @@ -390,12 +378,10 @@ public List getClasspathFiles() { } /** - * Returns all unique directories or zip/jarfiles on the classpath, in - * classloader resolution order, as a + * Returns all unique directories or zip/jarfiles on the classpath, in classloader resolution order, as a * classpath string, delineated with the standard path separator character. * - * @return a the unique directories and jarfiles on the classpath, in classpath - * resolution order, as a path + * @return a the unique directories and jarfiles on the classpath, in classpath resolution order, as a path * string. */ public String getClasspath() { @@ -430,10 +416,8 @@ public List getClasspathURIs() { } /** - * Returns an ordered list of unique classpath element and module URLs. Will - * skip any system modules or modules - * that are part of a jlink'd runtime image, since {@link URL} does not support - * the {@code jrt:} {@link URI} + * Returns an ordered list of unique classpath element and module URLs. Will skip any system modules or modules + * that are part of a jlink'd runtime image, since {@link URL} does not support the {@code jrt:} {@link URI} * scheme. * * @return The unique classpath element and module URLs. @@ -472,18 +456,13 @@ public List getModules() { } /** - * Get the module path info provided on the commandline with - * {@code --module-path}, {@code --add-modules}, - * {@code --patch-module}, {@code --add-exports}, {@code --add-opens}, and - * {@code --add-reads}, and also the - * {@code Add-Exports} and {@code Add-Opens} entries from jarfile manifest files - * encountered during scanning. + * Get the module path info provided on the commandline with {@code --module-path}, {@code --add-modules}, + * {@code --patch-module}, {@code --add-exports}, {@code --add-opens}, and {@code --add-reads}, and also the + * {@code Add-Exports} and {@code Add-Opens} entries from jarfile manifest files encountered during scanning. * *

- * Note that the returned {@link ModulePathInfo} object does not include - * classpath entries from the traditional - * classpath or system modules. Use {@link #getModules()} to get all visible - * modules, including anonymous, + * Note that the returned {@link ModulePathInfo} object does not include classpath entries from the traditional + * classpath or system modules. Use {@link #getModules()} to get all visible modules, including anonymous, * automatic and system modules. * * @return The {@link ModulePathInfo}. @@ -499,8 +478,7 @@ public ModulePathInfo getModulePathInfo() { /** * Get the list of all resources. * - * @return A list of all resources (including classfiles and non-classfiles) - * found in accepted packages. + * @return A list of all resources (including classfiles and non-classfiles) found in accepted packages. */ public ResourceList getAllResources() { if (allAcceptedResourcesCached == null) { @@ -516,12 +494,10 @@ public ResourceList getAllResources() { } /** - * Get a map from resource path to {@link Resource} for all resources (including - * classfiles and non-classfiles) + * Get a map from resource path to {@link Resource} for all resources (including classfiles and non-classfiles) * found in accepted packages. * - * @return The map from resource path to {@link Resource} for all resources - * (including classfiles and + * @return The map from resource path to {@link Resource} for all resources (including classfiles and * non-classfiles) found in accepted packages. */ public Map getAllResourcesAsMap() { @@ -541,18 +517,13 @@ public Map getAllResourcesAsMap() { } /** - * Get the list of all resources found in accepted packages that have the given - * path, relative to the package - * root of the classpath element. May match several resources, up to one per - * classpath element. + * Get the list of all resources found in accepted packages that have the given path, relative to the package + * root of the classpath element. May match several resources, up to one per classpath element. * * @param resourcePath - * A complete resource path, relative to the classpath entry - * package root. - * @return A list of all resources found in accepted packages that have the - * given path, relative to the package - * root of the classpath element. May match several resources, up to one - * per classpath element. + * A complete resource path, relative to the classpath entry package root. + * @return A list of all resources found in accepted packages that have the given path, relative to the package + * root of the classpath element. May match several resources, up to one per classpath element. */ public ResourceList getResourcesWithPath(final String resourcePath) { if (closed.get()) { @@ -583,27 +554,18 @@ public ResourceList getResourcesWithPath(final String resourcePath) { } /** - * Get the list of all resources found in any classpath element, whether in - * accepted packages or not (as long - * as the resource is not rejected), that have the given path, relative to - * the package root of the classpath - * element. May match several resources, up to one per classpath element. Note - * that this may not return a - * non-accepted resource, particularly when scanning directory classpath - * elements, because recursive scanning - * terminates once there are no possible accepted resources below a given - * directory. However, resources in + * Get the list of all resources found in any classpath element, whether in accepted packages or not (as long + * as the resource is not rejected), that have the given path, relative to the package root of the classpath + * element. May match several resources, up to one per classpath element. Note that this may not return a + * non-accepted resource, particularly when scanning directory classpath elements, because recursive scanning + * terminates once there are no possible accepted resources below a given directory. However, resources in * ancestral directories of accepted directories can be found using this method. * * @param resourcePath - * A complete resource path, relative to the classpath entry - * package root. - * @return A list of all resources found in any classpath element, whether in - * accepted packages or not (as - * long as the resource is not rejected), that have the given path, - * relative to the package root of - * the classpath element. May match several resources, up to one per - * classpath element. + * A complete resource path, relative to the classpath entry package root. + * @return A list of all resources found in any classpath element, whether in accepted packages or not (as + * long as the resource is not rejected), that have the given path, relative to the package root of + * the classpath element. May match several resources, up to one per classpath element. */ public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath) { if (closed.get()) { @@ -625,14 +587,10 @@ public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath * Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. * * @param resourcePath - * A complete resource path, relative to the classpath entry - * package root. - * @return A list of all resources found in any classpath element, whether in - * accepted packages or not (as - * long as the resource is not rejected), that have the given path, - * relative to the package root of - * the classpath element. May match several resources, up to one per - * classpath element. + * A complete resource path, relative to the classpath entry package root. + * @return A list of all resources found in any classpath element, whether in accepted packages or not (as + * long as the resource is not rejected), that have the given path, relative to the package root of + * the classpath element. May match several resources, up to one per classpath element. * @deprecated Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. */ @Deprecated @@ -641,13 +599,11 @@ public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourceP } /** - * Get the list of all resources found in accepted packages that have the - * requested leafname. + * Get the list of all resources found in accepted packages that have the requested leafname. * * @param leafName - * A resource leaf filename. - * @return A list of all resources found in accepted packages that have the - * requested leafname. + * A resource leaf filename. + * @return A list of all resources found in accepted packages that have the requested leafname. */ public ResourceList getResourcesWithLeafName(final String leafName) { if (closed.get()) { @@ -670,14 +626,11 @@ public ResourceList getResourcesWithLeafName(final String leafName) { } /** - * Get the list of all resources found in accepted packages that have the - * requested filename extension. + * Get the list of all resources found in accepted packages that have the requested filename extension. * * @param extension - * A filename extension, e.g. "xml" to match all resources - * ending in ".xml". - * @return A list of all resources found in accepted packages that have the - * requested filename extension. + * A filename extension, e.g. "xml" to match all resources ending in ".xml". + * @return A list of all resources found in accepted packages that have the requested filename extension. */ public ResourceList getResourcesWithExtension(final String extension) { if (closed.get()) { @@ -706,14 +659,12 @@ public ResourceList getResourcesWithExtension(final String extension) { } /** - * Get the list of all resources found in accepted packages that have a path - * matching the requested regex + * Get the list of all resources found in accepted packages that have a path matching the requested regex * pattern. See also {{@link #getResourcesMatchingWildcard(String)}. * * @param pattern - * A pattern to match {@link Resource} paths with. - * @return A list of all resources found in accepted packages that have a path - * matching the requested pattern. + * A pattern to match {@link Resource} paths with. + * @return A list of all resources found in accepted packages that have a path matching the requested pattern. */ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { if (closed.get()) { @@ -735,34 +686,26 @@ public ResourceList getResourcesMatchingPattern(final Pattern pattern) { } /** - * Get the list of all resources found in accepted packages that have a path - * matching the requested wildcard + * Get the list of all resources found in accepted packages that have a path matching the requested wildcard * string. * *

* The wildcard string may contain: *

    - *
  • Single asterisks, to match zero or more of any character other than - * '/'
  • + *
  • Single asterisks, to match zero or more of any character other than '/'
  • *
  • Double asterisks, to match zero or more of any character
  • *
  • Question marks, to match one character
  • - *
  • Any other regexp-style syntax, such as character sets (denoted by square - * brackets) -- the remainder of - * the expression is passed through to the Java regex parser, after escaping dot - * characters.
  • + *
  • Any other regexp-style syntax, such as character sets (denoted by square brackets) -- the remainder of + * the expression is passed through to the Java regex parser, after escaping dot characters.
  • *
* *

- * The wildcard string is translated in a simplistic way into a regex. If you - * need more complex pattern - * matching, use a regex directly, via - * {@link #getResourcesMatchingPattern(Pattern)}. + * The wildcard string is translated in a simplistic way into a regex. If you need more complex pattern + * matching, use a regex directly, via {@link #getResourcesMatchingPattern(Pattern)}. * * @param wildcardString - * A wildcard (glob) pattern to match {@link Resource} - * paths with. - * @return A list of all resources found in accepted packages that have a path - * matching the requested wildcard + * A wildcard (glob) pattern to match {@link Resource} paths with. + * @return A list of all resources found in accepted packages that have a path matching the requested wildcard * string. */ public ResourceList getResourcesMatchingWildcard(final String wildcardString) { @@ -776,14 +719,12 @@ public ResourceList getResourcesMatchingWildcard(final String wildcardString) { // Modules /** - * Get the {@link ModuleInfo} object for the named module, or null if no module - * of the requested name was found + * Get the {@link ModuleInfo} object for the named module, or null if no module of the requested name was found * during the scan. * * @param moduleName - * The module name. - * @return The {@link ModuleInfo} object for the named module, or null if the - * module was not found. + * The module name. + * @return The {@link ModuleInfo} object for the named module, or null if the module was not found. */ public ModuleInfo getModuleInfo(final String moduleName) { if (closed.get()) { @@ -798,8 +739,7 @@ public ModuleInfo getModuleInfo(final String moduleName) { /** * Get all modules found during the scan. * - * @return A list of all modules found during the scan, or the empty list if - * none. + * @return A list of all modules found during the scan, or the empty list if none. */ public ModuleInfoList getModuleInfo() { if (closed.get()) { @@ -815,14 +755,12 @@ public ModuleInfoList getModuleInfo() { // Packages /** - * Get the {@link PackageInfo} object for the named package, or null if no - * package of the requested name was + * Get the {@link PackageInfo} object for the named package, or null if no package of the requested name was * found during the scan. * * @param packageName - * The package name. - * @return The {@link PackageInfo} object for the named package, or null if the - * package was not found. + * The package name. + * @return The {@link PackageInfo} object for the named package, or null if the package was not found. */ public PackageInfo getPackageInfo(final String packageName) { if (closed.get()) { @@ -837,8 +775,7 @@ public PackageInfo getPackageInfo(final String packageName) { /** * Get all packages found during the scan. * - * @return A list of all packages found during the scan, or the empty list if - * none. + * @return A list of all packages found during the scan, or the empty list if none. */ public PackageInfoList getPackageInfo() { if (closed.get()) { @@ -854,24 +791,16 @@ public PackageInfoList getPackageInfo() { // Class dependencies /** - * Get a map from the {@link ClassInfo} object for each accepted class to a list - * of the classes referenced by - * that class (i.e. returns a map from dependents to dependencies). Note that - * you need to call - * {@link ClassGraph#enableInterClassDependencies()} before - * {@link ClassGraph#scan()} for this method to work. - * You should also call {@link ClassGraph#enableExternalClasses()} before - * {@link ClassGraph#scan()} if you want - * non-accepted classes to appear in the result. See also - * {@link #getReverseClassDependencyMap()}, which inverts + * Get a map from the {@link ClassInfo} object for each accepted class to a list of the classes referenced by + * that class (i.e. returns a map from dependents to dependencies). Note that you need to call + * {@link ClassGraph#enableInterClassDependencies()} before {@link ClassGraph#scan()} for this method to work. + * You should also call {@link ClassGraph#enableExternalClasses()} before {@link ClassGraph#scan()} if you want + * non-accepted classes to appear in the result. See also {@link #getReverseClassDependencyMap()}, which inverts * the map. * - * @return A map from a {@link ClassInfo} object for each accepted class to a - * list of the classes referenced by - * that class (i.e. returns a map from dependents to dependencies). Each - * map value is the result of - * calling {@link ClassInfo#getClassDependencies()} on the corresponding - * key. + * @return A map from a {@link ClassInfo} object for each accepted class to a list of the classes referenced by + * that class (i.e. returns a map from dependents to dependencies). Each map value is the result of + * calling {@link ClassInfo#getClassDependencies()} on the corresponding key. */ public Map getClassDependencyMap() { final Map map = new HashMap<>(); @@ -882,22 +811,15 @@ public Map getClassDependencyMap() { } /** - * Get the reverse class dependency map, i.e. a map from the {@link ClassInfo} - * object for each dependency class - * (accepted or not) to a list of the accepted classes that referenced that - * class as a dependency (i.e. returns + * Get the reverse class dependency map, i.e. a map from the {@link ClassInfo} object for each dependency class + * (accepted or not) to a list of the accepted classes that referenced that class as a dependency (i.e. returns * a map from dependencies to dependents). Note that you need to call - * {@link ClassGraph#enableInterClassDependencies()} before - * {@link ClassGraph#scan()} for this method to work. - * You should also call {@link ClassGraph#enableExternalClasses()} before - * {@link ClassGraph#scan()} if you want - * non-accepted classes to appear in the result. See also - * {@link #getClassDependencyMap}. + * {@link ClassGraph#enableInterClassDependencies()} before {@link ClassGraph#scan()} for this method to work. + * You should also call {@link ClassGraph#enableExternalClasses()} before {@link ClassGraph#scan()} if you want + * non-accepted classes to appear in the result. See also {@link #getClassDependencyMap}. * - * @return A map from a {@link ClassInfo} object for each dependency class - * (accepted or not) to a list of the - * accepted classes that referenced that class as a dependency (i.e. - * returns a map from dependencies to + * @return A map from a {@link ClassInfo} object for each dependency class (accepted or not) to a list of the + * accepted classes that referenced that class as a dependency (i.e. returns a map from dependencies to * dependents). */ public Map getReverseClassDependencyMap() { @@ -922,14 +844,12 @@ public Map getReverseClassDependencyMap() { // Classes /** - * Get the {@link ClassInfo} object for the named class, or null if no class of - * the requested name was found in + * Get the {@link ClassInfo} object for the named class, or null if no class of the requested name was found in * an accepted/non-rejected package during the scan. * * @param className - * The class name. - * @return The {@link ClassInfo} object for the named class, or null if the - * class was not found. + * The class name. + * @return The {@link ClassInfo} object for the named class, or null if the class was not found. */ public ClassInfo getClassInfo(final String className) { if (closed.get()) { @@ -944,8 +864,7 @@ public ClassInfo getClassInfo(final String className) { /** * Get all classes, interfaces and annotations found during the scan. * - * @return A list of all accepted classes found during the scan, or the empty - * list if none. + * @return A list of all accepted classes found during the scan, or the empty list if none. */ public ClassInfoList getAllClasses() { if (closed.get()) { @@ -960,8 +879,7 @@ public ClassInfoList getAllClasses() { /** * Get all {@link Enum} classes found during the scan. * - * @return A list of all {@link Enum} classes found during the scan, or the - * empty list if none. + * @return A list of all {@link Enum} classes found during the scan, or the empty list if none. */ public ClassInfoList getAllEnums() { if (closed.get()) { @@ -976,8 +894,7 @@ public ClassInfoList getAllEnums() { /** * Get all {@code record} classes found during the scan (JDK 14+). * - * @return A list of all {@code record} classes found during the scan, or the - * empty list if none. + * @return A list of all {@code record} classes found during the scan, or the empty list if none. */ public ClassInfoList getAllRecords() { if (closed.get()) { @@ -990,12 +907,10 @@ public ClassInfoList getAllRecords() { } /** - * Get a map from class name to {@link ClassInfo} object for all classes, - * interfaces and annotations found + * Get a map from class name to {@link ClassInfo} object for all classes, interfaces and annotations found * during the scan. * - * @return The map from class name to {@link ClassInfo} object for all classes, - * interfaces and annotations found + * @return The map from class name to {@link ClassInfo} object for all classes, interfaces and annotations found * during the scan. */ public Map getAllClassesAsMap() { @@ -1009,11 +924,9 @@ public Map getAllClassesAsMap() { } /** - * Get all standard (non-interface/non-annotation) classes found during the - * scan. + * Get all standard (non-interface/non-annotation) classes found during the scan. * - * @return A list of all accepted standard classes found during the scan, or the - * empty list if none. + * @return A list of all accepted standard classes found during the scan, or the empty list if none. */ public ClassInfoList getAllStandardClasses() { if (closed.get()) { @@ -1029,7 +942,7 @@ public ClassInfoList getAllStandardClasses() { * Get all subclasses of the superclass. * * @param superclass - * The superclass. + * The superclass. * @return A list of subclasses of the superclass, or the empty list if none. */ public ClassInfoList getSubclasses(final Class superclass) { @@ -1040,9 +953,8 @@ public ClassInfoList getSubclasses(final Class superclass) { * Get all subclasses of the named superclass. * * @param superclassName - * The name of the superclass. - * @return A list of subclasses of the named superclass, or the empty list if - * none. + * The name of the superclass. + * @return A list of subclasses of the named superclass, or the empty list if none. */ public ClassInfoList getSubclasses(final String superclassName) { if (closed.get()) { @@ -1064,9 +976,8 @@ public ClassInfoList getSubclasses(final String superclassName) { * Get superclasses of the named subclass. * * @param subclassName - * The name of the subclass. - * @return A list of superclasses of the named subclass, or the empty list if - * none. + * The name of the subclass. + * @return A list of superclasses of the named subclass, or the empty list if none. */ public ClassInfoList getSuperclasses(final String subclassName) { if (closed.get()) { @@ -1083,9 +994,8 @@ public ClassInfoList getSuperclasses(final String subclassName) { * Get superclasses of the subclass. * * @param subclass - * The subclass. - * @return A list of superclasses of the named subclass, or the empty list if - * none. + * The subclass. + * @return A list of superclasses of the named subclass, or the empty list if none. */ public ClassInfoList getSuperclasses(final Class subclass) { return getSuperclasses(subclass.getName()); @@ -1095,9 +1005,8 @@ public ClassInfoList getSuperclasses(final Class subclass) { * Get classes that have a method with an annotation of the named type. * * @param methodAnnotation - * the method annotation. - * @return A list of classes with a method that has an annotation of the named - * type, or the empty list if none. + * the method annotation. + * @return A list of classes with a method that has an annotation of the named type, or the empty list if none. */ public ClassInfoList getClassesWithMethodAnnotation(final Class methodAnnotation) { Assert.isAnnotation(methodAnnotation); @@ -1108,9 +1017,8 @@ public ClassInfoList getClassesWithMethodAnnotation(final Class fieldAnnotation) { Assert.isAnnotation(fieldAnnotation); @@ -1180,9 +1082,8 @@ public ClassInfoList getClassesWithFieldAnnotation(final Class classRef) { @@ -1257,14 +1150,12 @@ public ClassInfoList getInterfaces(final Class classRef) { } /** - * Get all classes that implement (or have superclasses that implement) the - * interface (or one of its + * Get all classes that implement (or have superclasses that implement) the interface (or one of its * subinterfaces). * * @param interfaceClass - * The interface class. - * @return A list of all classes that implement the interface, or the empty list - * if none. + * The interface class. + * @return A list of all classes that implement the interface, or the empty list if none. */ public ClassInfoList getClassesImplementing(final Class interfaceClass) { Assert.isInterface(interfaceClass); @@ -1272,14 +1163,12 @@ public ClassInfoList getClassesImplementing(final Class interfaceClass) { } /** - * Get all classes that implement (or have superclasses that implement) the - * named interface (or one of its + * Get all classes that implement (or have superclasses that implement) the named interface (or one of its * subinterfaces). * * @param interfaceName - * The interface name. - * @return A list of all classes that implement the named interface, or the - * empty list if none. + * The interface name. + * @return A list of all classes that implement the named interface, or the empty list if none. */ public ClassInfoList getClassesImplementing(final String interfaceName) { if (closed.get()) { @@ -1296,11 +1185,9 @@ public ClassInfoList getClassesImplementing(final String interfaceName) { // Annotations /** - * Get all annotation classes found during the scan. See also - * {@link #getAllInterfacesAndAnnotations()}. + * Get all annotation classes found during the scan. See also {@link #getAllInterfacesAndAnnotations()}. * - * @return A list of all annotation classes found during the scan, or the empty - * list if none. + * @return A list of all annotation classes found during the scan, or the empty list if none. */ public ClassInfoList getAllAnnotations() { if (closed.get()) { @@ -1314,12 +1201,10 @@ public ClassInfoList getAllAnnotations() { } /** - * Get all interface or annotation classes found during the scan. (Annotations - * are technically interfaces, and + * Get all interface or annotation classes found during the scan. (Annotations are technically interfaces, and * they can be implemented.) * - * @return A list of all accepted interfaces found during the scan, or the empty - * list if none. + * @return A list of all accepted interfaces found during the scan, or the empty list if none. */ public ClassInfoList getAllInterfacesAndAnnotations() { if (closed.get()) { @@ -1336,9 +1221,8 @@ public ClassInfoList getAllInterfacesAndAnnotations() { * Get classes with the class annotation or meta-annotation. * * @param annotation - * The class annotation or meta-annotation. - * @return A list of all non-annotation classes that were found with the class - * annotation during the scan, or + * The class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the class annotation during the scan, or * the empty list if none. */ public ClassInfoList getClassesWithAnnotation(final Class annotation) { @@ -1346,13 +1230,30 @@ public ClassInfoList getClassesWithAnnotation(final Class return getClassesWithAnnotation(annotation.getName()); } + /** + * Get classes with the class annotations or meta-annotation. + * + * @param annotations + * The class annotations or meta-annotations. + * @return A list of all non-annotation classes that were found with the class annotations during the scan, or + * the empty list if none. + */ + public ClassInfoList getClassesWithAnnotations( + @SuppressWarnings("unchecked") final Class... annotations) { + final List annotationNames = new ArrayList<>(); + for (final Class cls : annotations) { + Assert.isAnnotation(cls); + annotationNames.add(cls.getName()); + } + return getClassesWithAnnotations(annotationNames); + } + /** * Get classes with the named class annotation or meta-annotation. * * @param annotationName - * The name of the class annotation or meta-annotation. - * @return A list of all non-annotation classes that were found with the named - * class annotation during the scan, + * The name of the class annotation or meta-annotation. + * @return A list of all non-annotation classes that were found with the named class annotation during the scan, * or the empty list if none. */ public ClassInfoList getClassesWithAnnotation(final String annotationName) { @@ -1368,18 +1269,35 @@ public ClassInfoList getClassesWithAnnotation(final String annotationName) { } /** - * Get annotations on the named class. This only returns the annotating classes; - * to read annotation parameters, - * call {@link #getClassInfo(String)} to get the {@link ClassInfo} object for - * the named class, then if the - * {@link ClassInfo} object is non-null, call - * {@link ClassInfo#getAnnotationInfo()} to get detailed annotation + * Get classes with the named class annotations or meta-annotation. + * + * @param annotationNames + * The name of the class annotations or meta-annotations. + * @return A list of all non-annotation classes that were found with the named class annotations during the + * scan, or the empty list if none. + */ + public ClassInfoList getClassesWithAnnotations(final List annotationNames) { + ClassInfoList foundClassInfo = null; + for (final String annotationName : annotationNames) { + final ClassInfoList classInfoList = getClassesWithAnnotation(annotationName); + if (foundClassInfo == null) { + foundClassInfo = classInfoList; + } else { + foundClassInfo = foundClassInfo.intersect(classInfoList); + } + } + return foundClassInfo == null ? ClassInfoList.EMPTY_LIST : foundClassInfo; + } + + /** + * Get annotations on the named class. This only returns the annotating classes; to read annotation parameters, + * call {@link #getClassInfo(String)} to get the {@link ClassInfo} object for the named class, then if the + * {@link ClassInfo} object is non-null, call {@link ClassInfo#getAnnotationInfo()} to get detailed annotation * info. * * @param className - * The name of the class. - * @return A list of all annotation classes that were found with the named class - * annotation during the scan, or + * The name of the class. + * @return A list of all annotation classes that were found with the named class annotation during the scan, or * the empty list if none. */ public ClassInfoList getAnnotationsOnClass(final String className) { @@ -1398,16 +1316,12 @@ public ClassInfoList getAnnotationsOnClass(final String className) { // Classpath modification tests /** - * Determine whether the classpath contents have been modified since the last - * scan. Checks the timestamps of - * files and jarfiles encountered during the previous scan to see if they have - * changed. Does not perform a full - * scan, so cannot detect the addition of directories that newly match accept - * criteria -- you need to perform a + * Determine whether the classpath contents have been modified since the last scan. Checks the timestamps of + * files and jarfiles encountered during the previous scan to see if they have changed. Does not perform a full + * scan, so cannot detect the addition of directories that newly match accept criteria -- you need to perform a * full scan to detect those changes. * - * @return true if the classpath contents have been modified since the last - * scan. + * @return true if the classpath contents have been modified since the last scan. */ public boolean classpathContentsModifiedSinceScan() { if (closed.get()) { @@ -1426,21 +1340,16 @@ public boolean classpathContentsModifiedSinceScan() { } /** - * Find the maximum last-modified timestamp of any accepted - * file/directory/jarfile encountered during the scan. - * Checks the current timestamps, so this should increase between calls if - * something changes in accepted paths. - * Assumes both file and system timestamps were generated from clocks whose time - * was accurate. Ignores + * Find the maximum last-modified timestamp of any accepted file/directory/jarfile encountered during the scan. + * Checks the current timestamps, so this should increase between calls if something changes in accepted paths. + * Assumes both file and system timestamps were generated from clocks whose time was accurate. Ignores * timestamps greater than the system time. * *

- * This method cannot in general tell if classpath has changed (or modules have - * been added or removed) if it is + * This method cannot in general tell if classpath has changed (or modules have been added or removed) if it is * run twice during the same runtime session. * - * @return the maximum last-modified time for accepted files/directories/jars - * encountered during the scan. + * @return the maximum last-modified time for accepted files/directories/jars encountered during the scan. */ public long classpathContentsLastModifiedTime() { if (closed.get()) { @@ -1462,8 +1371,7 @@ public long classpathContentsLastModifiedTime() { // Classloading /** - * Get the ClassLoader order, respecting parent-first/parent-last delegation - * order. + * Get the ClassLoader order, respecting parent-first/parent-last delegation order. * * @return the class loader order. */ @@ -1472,40 +1380,26 @@ ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { } /** - * Load a class given a class name. If ignoreExceptions is false, and the class - * cannot be loaded (due to - * classloading error, or due to an exception being thrown in the class - * initialization block), an - * IllegalArgumentException is thrown; otherwise, the class will simply be - * skipped if an exception is thrown. + * Load a class given a class name. If ignoreExceptions is false, and the class cannot be loaded (due to + * classloading error, or due to an exception being thrown in the class initialization block), an + * IllegalArgumentException is thrown; otherwise, the class will simply be skipped if an exception is thrown. * *

- * Enable verbose scanning to see details of any exceptions thrown during - * classloading, even if ignoreExceptions + * Enable verbose scanning to see details of any exceptions thrown during classloading, even if ignoreExceptions * is false. * * @param className - * the class to load. + * the class to load. * @param returnNullIfClassNotFound - * If true, null is returned if there was an - * exception during classloading, otherwise - * IllegalArgumentException is thrown if a - * class could not be loaded. - * @return a reference to the loaded class, or null if the class could not be - * loaded and ignoreExceptions is + * If true, null is returned if there was an exception during classloading, otherwise + * IllegalArgumentException is thrown if a class could not be loaded. + * @return a reference to the loaded class, or null if the class could not be loaded and ignoreExceptions is * true. * @throws IllegalArgumentException - * if ignoreExceptions is false, - * IllegalArgumentException is thrown if there - * were problems loading - * or initializing the class. (Note that class - * initialization on load is disabled by - * default, you - * can enable it with - * {@code ClassGraph#initializeLoadedClasses(true)} - * .) Otherwise exceptions are - * suppressed, and null is returned if any of - * these problems occurs. + * if ignoreExceptions is false, IllegalArgumentException is thrown if there were problems loading + * or initializing the class. (Note that class initialization on load is disabled by default, you + * can enable it with {@code ClassGraph#initializeLoadedClasses(true)} .) Otherwise exceptions are + * suppressed, and null is returned if any of these problems occurs. */ public Class loadClass(final String className, final boolean returnNullIfClassNotFound) throws IllegalArgumentException { @@ -1527,45 +1421,31 @@ public Class loadClass(final String className, final boolean returnNullIfClas } /** - * Load a class given a class name. If ignoreExceptions is false, and the class - * cannot be loaded (due to - * classloading error, or due to an exception being thrown in the class - * initialization block), an - * IllegalArgumentException is thrown; otherwise, the class will simply be - * skipped if an exception is thrown. + * Load a class given a class name. If ignoreExceptions is false, and the class cannot be loaded (due to + * classloading error, or due to an exception being thrown in the class initialization block), an + * IllegalArgumentException is thrown; otherwise, the class will simply be skipped if an exception is thrown. * *

- * Enable verbose scanning to see details of any exceptions thrown during - * classloading, even if ignoreExceptions + * Enable verbose scanning to see details of any exceptions thrown during classloading, even if ignoreExceptions * is false. * * @param - * the superclass or interface type. + * the superclass or interface type. * @param className - * the class to load. + * the class to load. * @param superclassOrInterfaceType - * The class type to cast the result to. + * The class type to cast the result to. * @param returnNullIfClassNotFound - * If true, null is returned if there was an - * exception during classloading, otherwise - * IllegalArgumentException is thrown if a - * class could not be loaded. - * @return a reference to the loaded class, or null if the class could not be - * loaded and ignoreExceptions is + * If true, null is returned if there was an exception during classloading, otherwise + * IllegalArgumentException is thrown if a class could not be loaded. + * @return a reference to the loaded class, or null if the class could not be loaded and ignoreExceptions is * true. * @throws IllegalArgumentException - * if ignoreExceptions is false, - * IllegalArgumentException is thrown if there - * were problems loading - * the class, initializing the class, or - * casting it to the requested type. (Note that - * class - * initialization on load is disabled by - * default, you can enable it with - * {@code ClassGraph#initializeLoadedClasses(true)} - * .) Otherwise exceptions are suppressed, and - * null - * is returned if any of these problems occurs. + * if ignoreExceptions is false, IllegalArgumentException is thrown if there were problems loading + * the class, initializing the class, or casting it to the requested type. (Note that class + * initialization on load is disabled by default, you can enable it with + * {@code ClassGraph#initializeLoadedClasses(true)} .) Otherwise exceptions are suppressed, and null + * is returned if any of these problems occurs. */ public Class loadClass(final String className, final Class superclassOrInterfaceType, final boolean returnNullIfClassNotFound) throws IllegalArgumentException { @@ -1609,7 +1489,7 @@ public Class loadClass(final String className, final Class superclassO * Deserialize a ScanResult from previously-serialized JSON. * * @param json - * The JSON string for the serialized {@link ScanResult}. + * The JSON string for the serialized {@link ScanResult}. * @return The deserialized {@link ScanResult}. */ @SuppressWarnings("null") @@ -1681,8 +1561,7 @@ public static ScanResult fromJSON(final String json) { * Serialize a ScanResult to JSON. * * @param indentWidth - * If greater than 0, JSON will be formatted (indented), - * otherwise it will be minified (un-indented). + * If greater than 0, JSON will be formatted (indented), otherwise it will be minified (un-indented). * @return This {@link ScanResult}, serialized as a JSON string. */ public String toJSON(final int indentWidth) { @@ -1712,12 +1591,10 @@ public String toJSON() { } /** - * Checks if this {@link ScanResult} was obtained from JSON by deserialization, - * by calling + * Checks if this {@link ScanResult} was obtained from JSON by deserialization, by calling * {@link #fromJSON(String)}. * - * @return True if this {@link ScanResult} was obtained from JSON by - * deserialization. + * @return True if this {@link ScanResult} was obtained from JSON by deserialization. */ public boolean isObtainedFromDeserialization() { return isObtainedFromDeserialization; @@ -1726,12 +1603,9 @@ public boolean isObtainedFromDeserialization() { // ------------------------------------------------------------------------------------------------------------- /** - * Free any temporary files created by extracting jars or files from within - * jars. Without calling this method, - * the temporary files created by extracting the inner jars will be removed in a - * finalizer, called by the - * garbage collector (or at JVM shutdown). If you don't want to experience long - * GC pauses, make sure you call + * Free any temporary files created by extracting jars or files from within jars. Without calling this method, + * the temporary files created by extracting the inner jars will be removed in a finalizer, called by the + * garbage collector (or at JVM shutdown). If you don't want to experience long GC pauses, make sure you call * this close method when you have finished with the {@link ScanResult}. */ @Override @@ -1794,16 +1668,11 @@ public void close() { } /** - * Close all {@link ScanResult} instances that have not yet been closed. Note - * that this will close all open - * {@link ScanResult} instances for any class that uses the classloader that the - * {@link ScanResult} class is - * cached in -- so if you call this method, you need to ensure that the - * lifecycle of the classloader matches the - * lifecycle of your application, or that two concurrent applications don't - * share the same classloader, - * otherwise one application might close another application's - * {@link ScanResult} instances while they are still + * Close all {@link ScanResult} instances that have not yet been closed. Note that this will close all open + * {@link ScanResult} instances for any class that uses the classloader that the {@link ScanResult} class is + * cached in -- so if you call this method, you need to ensure that the lifecycle of the classloader matches the + * lifecycle of your application, or that two concurrent applications don't share the same classloader, + * otherwise one application might close another application's {@link ScanResult} instances while they are still * in use. */ public static void closeAll() { From b9555b2c3073d4019d849eaed75d8bcf1d0521fb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Feb 2024 02:45:06 -0700 Subject: [PATCH 590/713] Fix warning --- pom.xml | 4 ++-- src/main/java/io/github/classgraph/ScanResult.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index baf3fd083..78b2790ea 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.166-SNAPSHOT + 4.8.166 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.164 + classgraph-4.8.166 diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 365704905..8bbc43603 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1238,8 +1238,8 @@ public ClassInfoList getClassesWithAnnotation(final Class * @return A list of all non-annotation classes that were found with the class annotations during the scan, or * the empty list if none. */ - public ClassInfoList getClassesWithAnnotations( - @SuppressWarnings("unchecked") final Class... annotations) { + @SuppressWarnings("unchecked") + public ClassInfoList getClassesWithAnnotations(final Class... annotations) { final List annotationNames = new ArrayList<>(); for (final Class cls : annotations) { Assert.isAnnotation(cls); From 6c48ba0ebbe85fa54347c825eaea0db89e32e9c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Feb 2024 02:45:27 -0700 Subject: [PATCH 591/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 78b2790ea..e6aaa47a9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.166 + 4.8.167-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 8a32d1e9036486b21a78484f19c19f3754d4164e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Feb 2024 02:52:38 -0700 Subject: [PATCH 592/713] Fix deploy issue --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index e6aaa47a9..3a7f38bbe 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,11 @@ ossrh - https://oss.sonatype.org/content/repositories/snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -220,7 +220,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.12 + 1.6.13 org.apache.maven.plugins From 2fa72a4b4f91547b3f140b48a94f4ecf02264bf8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Feb 2024 11:26:25 -0700 Subject: [PATCH 593/713] Create all/any versions of annotated class finder --- .../java/io/github/classgraph/ScanResult.java | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 8bbc43603..5ad8449f4 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1231,21 +1231,39 @@ public ClassInfoList getClassesWithAnnotation(final Class } /** - * Get classes with the class annotations or meta-annotation. + * Get classes with all of the specified class annotations or meta-annotation. * * @param annotations * The class annotations or meta-annotations. - * @return A list of all non-annotation classes that were found with the class annotations during the scan, or - * the empty list if none. + * @return A list of all non-annotation classes that were found with any of the class annotations during the + * scan, or the empty list if none. */ @SuppressWarnings("unchecked") - public ClassInfoList getClassesWithAnnotations(final Class... annotations) { + public ClassInfoList getClassesWithAllAnnotations(final Class... annotations) { final List annotationNames = new ArrayList<>(); for (final Class cls : annotations) { Assert.isAnnotation(cls); annotationNames.add(cls.getName()); } - return getClassesWithAnnotations(annotationNames); + return getClassesWithAllAnnotations(annotationNames); + } + + /** + * Get classes with any of the specified class annotations or meta-annotation. + * + * @param annotations + * The class annotations or meta-annotations. + * @return A list of all non-annotation classes that were found with any of the class annotations during the + * scan, or the empty list if none. + */ + @SuppressWarnings("unchecked") + public ClassInfoList getClassesWithAnyAnnotation(final Class... annotations) { + final List annotationNames = new ArrayList<>(); + for (final Class cls : annotations) { + Assert.isAnnotation(cls); + annotationNames.add(cls.getName()); + } + return getClassesWithAnyAnnotation(annotationNames); } /** @@ -1269,14 +1287,14 @@ public ClassInfoList getClassesWithAnnotation(final String annotationName) { } /** - * Get classes with the named class annotations or meta-annotation. + * Get classes with all of the named class annotations or meta-annotation. * * @param annotationNames * The name of the class annotations or meta-annotations. - * @return A list of all non-annotation classes that were found with the named class annotations during the - * scan, or the empty list if none. + * @return A list of all non-annotation classes that were found with all of the named class annotations during + * the scan, or the empty list if none. */ - public ClassInfoList getClassesWithAnnotations(final List annotationNames) { + public ClassInfoList getClassesWithAllAnnotations(final List annotationNames) { ClassInfoList foundClassInfo = null; for (final String annotationName : annotationNames) { final ClassInfoList classInfoList = getClassesWithAnnotation(annotationName); @@ -1286,6 +1304,29 @@ public ClassInfoList getClassesWithAnnotations(final List annotationName foundClassInfo = foundClassInfo.intersect(classInfoList); } } + CollectionUtils.sortIfNotEmpty(foundClassInfo); + return foundClassInfo == null ? ClassInfoList.EMPTY_LIST : foundClassInfo; + } + + /** + * Get classes with any of the named class annotations or meta-annotation. + * + * @param annotationNames + * The name of the class annotations or meta-annotations. + * @return A list of all non-annotation classes that were found with any of the named class annotations during + * the scan, or the empty list if none. + */ + public ClassInfoList getClassesWithAnyAnnotation(final List annotationNames) { + ClassInfoList foundClassInfo = null; + for (final String annotationName : annotationNames) { + final ClassInfoList classInfoList = getClassesWithAnnotation(annotationName); + if (foundClassInfo == null) { + foundClassInfo = classInfoList; + } else { + foundClassInfo = foundClassInfo.union(classInfoList); + } + } + CollectionUtils.sortIfNotEmpty(foundClassInfo); return foundClassInfo == null ? ClassInfoList.EMPTY_LIST : foundClassInfo; } From 2147ac9e4ae9117191641342ed5655107e5f3ede Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Mar 2024 13:49:32 -0700 Subject: [PATCH 594/713] Bump version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a7f38bbe..2c608f0c5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.167-SNAPSHOT + 4.8.166-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 1e83791a7a15bd2b5d83e4983725ccfef7bc4bbf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Mar 2024 13:58:29 -0700 Subject: [PATCH 595/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d29e376b7..4d426dbec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java }} From d6331d39fd8b98f511db6d246614ce643b1389ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:49:31 +0000 Subject: [PATCH 596/713] Bump org.codehaus.mojo:build-helper-maven-plugin from 3.4.0 to 3.5.0 Bumps [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.4.0...3.5.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5aaa349a..1580ed199 100644 --- a/pom.xml +++ b/pom.xml @@ -190,7 +190,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.4.0 + 3.5.0 org.apache.maven.plugins From 13e8d1398b7094526d6d93d738d58528f94d73ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:49:35 +0000 Subject: [PATCH 597/713] Bump org.eclipse.jdt:org.eclipse.jdt.annotation from 2.2.700 to 2.2.800 Bumps [org.eclipse.jdt:org.eclipse.jdt.annotation](https://github.com/eclipse-jdt/eclipse.jdt.core) from 2.2.700 to 2.2.800. - [Commits](https://github.com/eclipse-jdt/eclipse.jdt.core/commits) --- updated-dependencies: - dependency-name: org.eclipse.jdt:org.eclipse.jdt.annotation dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5aaa349a..752efaac2 100644 --- a/pom.xml +++ b/pom.xml @@ -156,7 +156,7 @@ org.eclipse.jdt org.eclipse.jdt.annotation - 2.2.700 + 2.2.800 provided From b4b9fc8a9a228f9308d2d6bb716794ba8c1b5812 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:49:38 +0000 Subject: [PATCH 598/713] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.5 Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.2 to 3.2.5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5aaa349a..51c4cc9ed 100644 --- a/pom.xml +++ b/pom.xml @@ -185,7 +185,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.2 + 3.2.5 org.codehaus.mojo From 103a55c7e57035fb0c954c21a477e8939103f615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:49:41 +0000 Subject: [PATCH 599/713] Bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.11.0 to 3.12.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.11.0...maven-compiler-plugin-3.12.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5aaa349a..9d7461a97 100644 --- a/pom.xml +++ b/pom.xml @@ -180,7 +180,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.12.1 org.apache.maven.plugins From f503c7bcedb890187a6743a18000e2b818804bc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 02:49:44 +0000 Subject: [PATCH 600/713] Bump org.apache.maven.plugins:maven-clean-plugin from 3.3.1 to 3.3.2 Bumps [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.3.1...maven-clean-plugin-3.3.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5aaa349a..2566ffb1f 100644 --- a/pom.xml +++ b/pom.xml @@ -232,7 +232,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.3.1 + 3.3.2 org.apache.maven.plugins From a1b5f082973f69e01a3c372d2b04e7c010172cea Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 10:03:24 -0700 Subject: [PATCH 601/713] Change Nexus URL --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6fbbe34f7..700b9d450 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,11 @@ ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots + https://oss.sonatype.org/content/repositories/snapshots ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -554,7 +554,7 @@ true ossrh - https://s01.oss.sonatype.org/ + https://oss.sonatype.org/ true 10 From b978b8923b9a16a30e439b83a62c51d99510c721 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 10:06:14 -0700 Subject: [PATCH 602/713] [maven-release-plugin] prepare release classgraph-4.8.166 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 700b9d450..93e4520bb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.166-SNAPSHOT + 4.8.166 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 2c77ec703bac47e27c947242e3dc6f6d57b9d955 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 10:06:17 -0700 Subject: [PATCH 603/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93e4520bb..3d982da6e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.166 + 4.8.167-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 02f1f763eb874933820d122ca1fc141226ee0bff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 10:59:53 -0700 Subject: [PATCH 604/713] Make API from prev release consistent --- src/main/java/io/github/classgraph/ScanResult.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 5ad8449f4..b9e10fa18 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1245,7 +1245,7 @@ public ClassInfoList getClassesWithAllAnnotations(final Class annotationNames) { + public ClassInfoList getClassesWithAllAnnotations(final String... annotationNames) { ClassInfoList foundClassInfo = null; for (final String annotationName : annotationNames) { final ClassInfoList classInfoList = getClassesWithAnnotation(annotationName); @@ -1316,7 +1316,7 @@ public ClassInfoList getClassesWithAllAnnotations(final List annotationN * @return A list of all non-annotation classes that were found with any of the named class annotations during * the scan, or the empty list if none. */ - public ClassInfoList getClassesWithAnyAnnotation(final List annotationNames) { + public ClassInfoList getClassesWithAnyAnnotation(final String... annotationNames) { ClassInfoList foundClassInfo = null; for (final String annotationName : annotationNames) { final ClassInfoList classInfoList = getClassesWithAnnotation(annotationName); From d071bf4a5fb2bc63a3ea5ab8c7ff73abb9a2ab3b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 11:01:26 -0700 Subject: [PATCH 605/713] [maven-release-plugin] prepare release classgraph-4.8.167 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3d982da6e..3c9a54cbb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.167-SNAPSHOT + 4.8.167 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.166 + classgraph-4.8.167 From 8bf6dc22de25a1fce04cfe23d88bab4281f03408 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 11:01:29 -0700 Subject: [PATCH 606/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c9a54cbb..cc32630bf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.167 + 4.8.168-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 586fecc0b6cea9dec17607007b160c6e0f9471ce Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 15:07:27 -0700 Subject: [PATCH 607/713] Attempt to fix logic for override AppClassLoader(#795) --- .../classgraph/classpath/ClasspathFinder.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 8e64592c6..35323c5f5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -136,22 +136,23 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection // It's not possible to instantiate AppClassLoader or PlatformClassLoader, so if these are // passed in as override classloaders, they must have been obtained using // Thread.currentThread().getContextClassLoader() [.getParent()] or similar + if (!scanSpec.enableSystemJarsAndModules) { + if (classpathFinderLog != null && classLoaderClassName + .equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { + classpathFinderLog + .log("overrideClassLoaders() was called with an instance of " + classLoaderClassName + + ", so enableSystemJarsAndModules() was called automatically"); + } + scanSpec.enableSystemJarsAndModules = true; + } if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader") || classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { - if (!scanSpec.enableSystemJarsAndModules) { - if (classpathFinderLog != null) { - classpathFinderLog.log("overrideClassLoaders() was called with an instance of " - + classLoaderClassName + ", which is a system classloader, so " - + "enableSystemJarsAndModules() was called automatically"); - } - scanSpec.enableSystemJarsAndModules = true; - } - forceScanJavaClassPath = true; if (classpathFinderLog != null) { - classpathFinderLog.log("overrideClassLoaders() was called with an instance of " - + classLoaderClassName + ", which is a system classloader, so the " - + "`java.lang.path` classpath will also be scanned"); + classpathFinderLog + .log("overrideClassLoaders() was called with an instance of " + classLoaderClassName + + ", so the `java.class.path` classpath will also be scanned"); } + forceScanJavaClassPath = true; } } } else { @@ -295,8 +296,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection // and the classpath is not overridden, unless only module scanning was enabled, and an unnamed module // layer was encountered -- in this case, have to forcibly scan java.class.path, since the ModuleLayer // API doesn't allow for the opening of unnamed modules. - if ((!scanSpec.ignoreParentClassLoaders && (scanSpec.overrideClassLoaders == null || forceScanJavaClassPath) - && scanSpec.overrideClasspath == null) + if (forceScanJavaClassPath + || (!scanSpec.ignoreParentClassLoaders && scanSpec.overrideClassLoaders == null + && scanSpec.overrideClasspath == null) || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { From 282fe6770036bebf69259038a64348b6d1c1a411 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:34:42 +0000 Subject: [PATCH 608/713] Bump org.slf4j:slf4j-api from 2.0.11 to 2.0.12 Bumps org.slf4j:slf4j-api from 2.0.11 to 2.0.12. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc32630bf..c9fc6764e 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.11 + 2.0.12 test From 4515bbf28a4034968bb049f890200c64bb068307 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:34:45 +0000 Subject: [PATCH 609/713] Bump org.slf4j:slf4j-jdk14 from 2.0.7 to 2.0.12 Bumps org.slf4j:slf4j-jdk14 from 2.0.7 to 2.0.12. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc32630bf..bd6e3d3d2 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.7 + 2.0.12 test From 93862cc171555478123634e3e605d4c9aec87f88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:34:48 +0000 Subject: [PATCH 610/713] Bump org.junit.jupiter:junit-jupiter from 5.9.3 to 5.10.2 Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.9.3 to 5.10.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.3...r5.10.2) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc32630bf..e8e7be520 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ org.junit.jupiter junit-jupiter - 5.9.3 + 5.10.2 test From e0cb4ae6f04718f2face3253828d867e067111b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:34:52 +0000 Subject: [PATCH 611/713] Bump org.openjdk.jmh:jmh-generator-annprocess from 1.36 to 1.37 Bumps [org.openjdk.jmh:jmh-generator-annprocess](https://github.com/openjdk/jmh) from 1.36 to 1.37. - [Commits](https://github.com/openjdk/jmh/compare/1.36...1.37) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc32630bf..927924b97 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.36 + 1.37 test From 8c497031c8f3281c260a653e1026ce063e188d28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:34:56 +0000 Subject: [PATCH 612/713] Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.3.0 to 3.4.1 Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.3.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.3.0...enforcer-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc32630bf..53d696689 100644 --- a/pom.xml +++ b/pom.xml @@ -170,7 +170,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.3.0 + 3.4.1 org.apache.maven.plugins From 81093145da1950985429355d143eb3c038976475 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2024 20:31:13 -0700 Subject: [PATCH 613/713] Fix previous commit (#795) --- .../nonapi/io/github/classgraph/classpath/ClasspathFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 35323c5f5..3f54b04b1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -142,8 +142,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection classpathFinderLog .log("overrideClassLoaders() was called with an instance of " + classLoaderClassName + ", so enableSystemJarsAndModules() was called automatically"); + scanSpec.enableSystemJarsAndModules = true; } - scanSpec.enableSystemJarsAndModules = true; } if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader") || classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { From 81ced41eeacfedb6303db233d5be2f9ba2c26f47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 02:25:22 +0000 Subject: [PATCH 614/713] Bump org.assertj:assertj-core from 3.24.2 to 3.25.3 Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.24.2 to 3.25.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.24.2...assertj-build-3.25.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c35497515..c2136c1cc 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ org.assertj assertj-core - 3.24.2 + 3.25.3 test From e9f032b7ac866b4985b9588b94b3191c8c695338 Mon Sep 17 00:00:00 2001 From: Michael Musenbrock Date: Thu, 7 Mar 2024 15:17:51 +0100 Subject: [PATCH 615/713] Fix library detection in ClasspathElementDir. fixes #701 Signed-off-by: Michael Musenbrock --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 74b4a1e8f..1ec53d839 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -112,7 +112,7 @@ void open(final WorkQueue workQueue, final LogNode log) // Add all jarfiles within the lib dir as child classpath entries try (DirectoryStream stream = Files.newDirectoryStream(libDirPath)) { for (final Path filePath : stream) { - if (Files.isRegularFile(filePath) && filePath.getFileName().endsWith(".jar")) { + if (Files.isRegularFile(filePath) && filePath.toString().toLowerCase().endsWith(".jar")) { if (log != null) { log(classpathElementIdx, "Found lib jar: " + filePath, log); } From 8e58d436a625fa9bb208cede5b21f6dffb3a840a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Mar 2024 17:14:18 -0700 Subject: [PATCH 616/713] Fix test --- .../java/io/github/classgraph/issues/issue420/Issue420Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java index 8e53da1eb..f4cecb075 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -96,7 +96,7 @@ private void testDir(final String packageRootPrefix) throws IOException, URISynt final String packagePath = packageName.replace('.', '/'); final String classFullyQualifiedName = packageName + ".CompiledWithJDK8"; final String classFilePath = classFullyQualifiedName.replace('.', '/') + ".class"; - final Path jarPath = Paths.get(getClass().getClassLoader().getResource(classFilePath).toURI()); + final Path jarPath = Paths.get(Issue420Test.class.getClassLoader().getResource(classFilePath).toURI()); final Path memFsDirPath = memFs.getPath(packageRootPrefix + packagePath); Files.createDirectories(memFsDirPath); final Path memFsFilePath = memFs.getPath(memFsDirPath + "/" + className + ".class"); From de2651a2b82e13c746388e92912709b9070810cd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Mar 2024 17:14:44 -0700 Subject: [PATCH 617/713] [maven-release-plugin] prepare release classgraph-4.8.168 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c2136c1cc..72397f3a6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.168-SNAPSHOT + 4.8.168 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.167 + classgraph-4.8.168 From 287824f1ed34aa1ed133a9f77003294787815aba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 7 Mar 2024 17:14:46 -0700 Subject: [PATCH 618/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 72397f3a6..7078b8cd2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.168 + 4.8.169-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.168 + classgraph-4.8.167 From dea09dedbb129e2831541ec1dee816d2a41a8a60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 02:48:57 +0000 Subject: [PATCH 619/713] Bump org.eclipse.jdt:org.eclipse.jdt.annotation from 2.2.800 to 2.3.0 Bumps [org.eclipse.jdt:org.eclipse.jdt.annotation](https://github.com/eclipse-jdt/eclipse.jdt.core) from 2.2.800 to 2.3.0. - [Commits](https://github.com/eclipse-jdt/eclipse.jdt.core/commits) --- updated-dependencies: - dependency-name: org.eclipse.jdt:org.eclipse.jdt.annotation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7078b8cd2..3c05d93ed 100644 --- a/pom.xml +++ b/pom.xml @@ -156,7 +156,7 @@ org.eclipse.jdt org.eclipse.jdt.annotation - 2.2.800 + 2.3.0 provided From 6952b30c574351b2638d793e6d8b271a9a204602 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 02:49:00 +0000 Subject: [PATCH 620/713] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.1.0 to 3.2.0 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.1.0...maven-gpg-plugin-3.2.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7078b8cd2..a1d37a3a7 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.1.0 + 3.2.0 org.sonatype.plugins From 052bf4621e0d27d2af01e71f103a930647fb207e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Mar 2024 18:23:38 -0600 Subject: [PATCH 621/713] Fix for enableSystemJarsAndModules (#795) --- .../io/github/classgraph/classpath/ClasspathFinder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 3f54b04b1..189d9e102 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -136,14 +136,14 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection // It's not possible to instantiate AppClassLoader or PlatformClassLoader, so if these are // passed in as override classloaders, they must have been obtained using // Thread.currentThread().getContextClassLoader() [.getParent()] or similar - if (!scanSpec.enableSystemJarsAndModules) { - if (classpathFinderLog != null && classLoaderClassName - .equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { + if (!scanSpec.enableSystemJarsAndModules && classLoaderClassName + .equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { + if (classpathFinderLog != null) { classpathFinderLog .log("overrideClassLoaders() was called with an instance of " + classLoaderClassName + ", so enableSystemJarsAndModules() was called automatically"); - scanSpec.enableSystemJarsAndModules = true; } + scanSpec.enableSystemJarsAndModules = true; } if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader") || classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { From ad7ec172b59c3012e3296ec78d68f0436f2b8d0e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Mar 2024 22:58:02 -0600 Subject: [PATCH 622/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da6f5d2fb..3e82e3bc5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.169-SNAPSHOT + 4.8.170-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From a2ca4ed754c969e7d3ec572d452c3494f4723c31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 02:35:43 +0000 Subject: [PATCH 623/713] Bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.12.1 to 3.13.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.12.1...maven-compiler-plugin-3.13.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e82e3bc5..aba2f4f43 100644 --- a/pom.xml +++ b/pom.xml @@ -180,7 +180,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 org.apache.maven.plugins From 6469440cd4b0e8ab2c5e8ba34174bc1228a8c614 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 02:07:23 +0000 Subject: [PATCH 624/713] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.0 to 3.2.2 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.0 to 3.2.2. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.0...maven-gpg-plugin-3.2.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e82e3bc5..fbfb6ec5d 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.0 + 3.2.2 org.sonatype.plugins From 1aab2d42c96bb3e0d56a456ecbf3a8a1699c7129 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 3 Apr 2024 16:28:52 -0700 Subject: [PATCH 625/713] Move cenSize check after reading the zip64 eocd If archive is in ZIP64 format, the cenSize value may be 0xFFFFFFFF indicating the size will be in the corresponding zip64 end of central directory field. Fixes #841 --- .../classgraph/fastzipfilereader/LogicalZipFile.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java index 1281dd049..007224d6f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -486,10 +486,6 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } long cenSize = reader.readUnsignedInt(eocdPos + 12); - if (cenSize > eocdPos) { - throw new IOException( - "Central directory size out of range: " + cenSize + " vs. " + eocdPos + ": " + getPath()); - } long cenOff = reader.readUnsignedInt(eocdPos + 16); long cenPos = eocdPos - cenSize; @@ -536,6 +532,11 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final } } + if (cenSize > eocdPos) { + throw new IOException( + "Central directory size out of range: " + cenSize + " vs. " + eocdPos + ": " + getPath()); + } + // Get offset of first local file header final long locPos = cenPos - cenOff; if (locPos < 0) { From da9a1b4cb109d9e4fbe0f317031ae183ae7e7aeb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 3 Apr 2024 19:12:18 -0600 Subject: [PATCH 626/713] [maven-release-plugin] prepare release classgraph-4.8.170 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6c1c0a5c8..486900781 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.170-SNAPSHOT + 4.8.170 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.167 + classgraph-4.8.170 From a13e322de2e01359d695b736b3496e6a6d63bbea Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 3 Apr 2024 19:12:21 -0600 Subject: [PATCH 627/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 486900781..d488eb0cc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.170 + 4.8.171-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.170 + classgraph-4.8.167 From 2c019ab72774bd4bd05e776bd97b339115f42676 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 02:13:58 +0000 Subject: [PATCH 628/713] Bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.0 to 3.3.1. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.0...maven-source-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d488eb0cc..c044ca02b 100644 --- a/pom.xml +++ b/pom.xml @@ -205,7 +205,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins From 4c232b9910bd830257b33831542554f6e6d61bd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 02:38:01 +0000 Subject: [PATCH 629/713] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.2 to 3.2.3 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.2 to 3.2.3. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.2...maven-gpg-plugin-3.2.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c044ca02b..a6c3c0b2c 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.2 + 3.2.3 org.sonatype.plugins From 0fb082c7f9f6d1920381e21e44a2794db1a2e14c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 02:57:46 +0000 Subject: [PATCH 630/713] Bump org.slf4j:slf4j-jdk14 from 2.0.12 to 2.0.13 Bumps org.slf4j:slf4j-jdk14 from 2.0.12 to 2.0.13. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-jdk14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c044ca02b..6378cb5ba 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.slf4j slf4j-jdk14 - 2.0.12 + 2.0.13 test From 9a29c3c0b7feaee4fe1dd358b58a5b59f99cc375 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 02:57:49 +0000 Subject: [PATCH 631/713] Bump org.slf4j:slf4j-api from 2.0.12 to 2.0.13 Bumps org.slf4j:slf4j-api from 2.0.12 to 2.0.13. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c044ca02b..4d61cf1fb 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ org.slf4j slf4j-api - 2.0.12 + 2.0.13 test From c40ec7ec897b558e31c59202ac6433512d21b793 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 02:57:53 +0000 Subject: [PATCH 632/713] Bump org.apache.maven.plugins:maven-jar-plugin from 3.3.0 to 3.4.0 Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.3.0...maven-jar-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c044ca02b..7af8d682b 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.0 org.apache.maven.plugins From ec46faad55cd60dd576383cc42791da69cec97d0 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 14:21:19 +0200 Subject: [PATCH 633/713] Prefer File usage to Files in FileUtils Even though the Files API might be considered as a newer variant, but for example on Windows systems certain methods are four times slower than the File counterparts. --- .../io/github/classgraph/utils/FileUtils.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 73707842f..c97470284 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -300,6 +300,10 @@ public static boolean canRead(final File file) { * @return true if the file exists and can be read. */ public static boolean canRead(final Path path) { + try { + return canRead(path.toFile()); + } catch (UnsupportedOperationException ignored) { + } try { if (!Files.isReadable(path)) { return false; @@ -336,6 +340,10 @@ public static boolean canReadAndIsFile(final File file) { * @return true if the file exists, is a regular file, and can be read. */ public static boolean canReadAndIsFile(final Path path) { + try { + return canReadAndIsFile(path.toFile()); + } catch (UnsupportedOperationException ignored) { + } try { if (!Files.isReadable(path)) { return false; @@ -376,6 +384,11 @@ public static void checkCanReadAndIsFile(final File file) throws IOException { * if the path does not exist, is not a regular file, or cannot be read. */ public static void checkCanReadAndIsFile(final Path path) throws IOException { + try { + checkCanReadAndIsFile(path.toFile()); + return; + } catch (UnsupportedOperationException ignored) { + } try { if (!Files.isReadable(path)) { throw new FileNotFoundException("Path does not exist or cannot be read: " + path); @@ -414,6 +427,10 @@ public static boolean canReadAndIsDir(final File file) { * @return true if the file exists, is a directory, and can be read. */ public static boolean canReadAndIsDir(final Path path) { + try { + return canReadAndIsDir(path.toFile()); + } catch (UnsupportedOperationException ignored) { + } try { if (!Files.isReadable(path)) { return false; From 9c432ca27202ab1d3cae358868cc46d26066166a Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 14:21:50 +0200 Subject: [PATCH 634/713] Remove redundant isRegularFile check from canRead --- .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index c97470284..162a7f860 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -305,13 +305,10 @@ public static boolean canRead(final Path path) { } catch (UnsupportedOperationException ignored) { } try { - if (!Files.isReadable(path)) { - return false; - } + return Files.isReadable(path); } catch (final SecurityException e) { return false; } - return Files.isRegularFile(path); } /** From d9d536f8a0c63774e56619b51c463f2e714892c8 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 14:26:57 +0200 Subject: [PATCH 635/713] Optimize file attributes access of classpath entry objects --- .../java/io/github/classgraph/Scanner.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 36411dec6..51d0959bc 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -34,10 +34,14 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -548,16 +552,19 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, throw new IOException("Ignoring JrtFS filesystem path " + "(modules are scanned using the JPMS API): " + path); } - if (FileUtils.canReadAndIsFile(path)) { - // classpathEntObj is a Path which points to a file, so it must be a jar - isJar = true; - } else if (FileUtils.canReadAndIsDir(path)) { - // classpathEntObj is a Path which points to a dir - isJar = false; - } else if (!FileUtils.canRead(path)) { + if (!FileUtils.canRead(path)) { throw new IOException("Cannot read path: " + path); } else { - throw new IOException("Not a file or directory: " + path); + BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class); + if (attributes.isRegularFile()) { + // classpathEntObj is a Path which points to a file, so it must be a jar + isJar = true; + } else if (attributes.isDirectory()) { + // classpathEntObj is a Path which points to a dir + isJar = false; + } else { + throw new IOException("Not a file or directory: " + path); + } } } else { // Should not happen From bddbf5d7dc2d7c06ecf1d6e54659e489c4ec6306 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 14:28:49 +0200 Subject: [PATCH 636/713] Remove redundant checkCanReadAndIsFile from PhysicalZipFile Both FileSlice & PathSlice do the same, so no need for this additional check. --- .../classgraph/fastzipfilereader/PhysicalZipFile.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java index d4b99638f..df212f4bd 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -78,10 +78,6 @@ class PhysicalZipFile { PhysicalZipFile(final File file, final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; - - // Make sure the File is readable and is a regular file - FileUtils.checkCanReadAndIsFile(file); - this.file = file; this.pathStr = FastPathResolver.resolve(FileUtils.currDirPath(), file.getPath()); this.slice = new FileSlice(file, nestedJarHandler, log); @@ -102,10 +98,6 @@ class PhysicalZipFile { PhysicalZipFile(final Path path, final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; - - // Make sure the File is readable and is a regular file - FileUtils.checkCanReadAndIsFile(path); - this.path = path; this.pathStr = FastPathResolver.resolve(FileUtils.currDirPath(), path.toString()); this.slice = new PathSlice(path, nestedJarHandler); From 803a10295b3d3e80894d822cce5d5fd3a8296372 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 14:38:24 +0200 Subject: [PATCH 637/713] Remove redundant read call from Resources of ClasspathElementDir Probably it was there only to set the open state to true, so that the close works as intended. --- .../classgraph/ClasspathElementDir.java | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 1ec53d839..7ff99aa46 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -212,14 +212,8 @@ public Set getPosixFilePermissions() { @Override public ByteBuffer read() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkSkipState(); + markAsOpen(); pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; byteBuffer = pathSlice.read(); @@ -228,14 +222,8 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkSkipState(); + markAsOpen(); // Classfile won't be compressed, so wrap it in a new PathSlice and then open it pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; @@ -244,14 +232,8 @@ ClassfileReader openClassfile() throws IOException { @Override public InputStream open() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkSkipState(); + markAsOpen(); pathSlice = new PathSlice(resourcePath, nestedJarHandler); inputStream = pathSlice.open(this); length = pathSlice.sliceLength; @@ -260,7 +242,8 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { - read(); + checkSkipState(); + markAsOpen(); try (Resource res = this) { // Close this after use pathSlice = new PathSlice(resourcePath, nestedJarHandler); final byte[] bytes = pathSlice.load(); @@ -286,6 +269,20 @@ public void close() { super.close(); } } + + private void checkSkipState() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Parent directory could not be opened"); + } + } + + private void markAsOpen() throws IOException { + if (isOpen.getAndSet(true)) { + throw new IOException( + "Resource is already open -- cannot open it again without first calling close()"); + } + } }; } From f23ad987986eb8381da6a015bc0a18b755e1fb4b Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Tue, 16 Apr 2024 15:06:27 +0200 Subject: [PATCH 638/713] Reduce Disk I/O usage in ClasspathElementDir The file attributes are already used when creating a new Resource so we can easily pass it to the Resource for further usage, thus can share the already loaded data for a Path. If from a subPath it turns out to be a file, then we do not need to check at the end whether it's a directory so we now remove the files from the pathsInDir. --- .../classgraph/ClasspathElementDir.java | 133 ++++++++++++++---- .../io/github/classgraph/utils/FileUtils.java | 20 +++ 2 files changed, 125 insertions(+), 28 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 7ff99aa46..2be0cb235 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -37,15 +37,20 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; @@ -156,24 +161,29 @@ void open(final WorkQueue workQueue, final LogNode log) * * @param resourcePath * the {@link Path} for the resource - * @param nestedJarHandler - * the nested jar handler * @return the resource */ - private Resource newResource(final Path resourcePath, final NestedJarHandler nestedJarHandler) { - long length; - try { - length = Files.size(resourcePath); - } catch (IOException | SecurityException e) { - length = -1L; - } - return new Resource(this, length) { + private Resource newResource(final Path resourcePath, BasicFileAttributes attributes) { + final int notYetLoadedLength = -2; + return new Resource(this, attributes == null ? notYetLoadedLength : attributes.size()) { /** The {@link PathSlice} opened on the file. */ private PathSlice pathSlice; /** True if the resource is open. */ private final AtomicBoolean isOpen = new AtomicBoolean(); + @Override + public long getLength() { + if (length == notYetLoadedLength) { + try { + length = Files.size(resourcePath); + } catch (IOException | SecurityException e) { + length = -1; + } + } + return length; + } + @Override public String getPath() { String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); @@ -191,7 +201,7 @@ public String getPathRelativeToClasspathElement() { @Override public long getLastModified() { try { - return resourcePath.toFile().lastModified(); + return attributes == null ? resourcePath.toFile().lastModified() : attributes.lastModifiedTime().toMillis(); } catch (final UnsupportedOperationException e) { return 0L; } @@ -202,8 +212,12 @@ public long getLastModified() { public Set getPosixFilePermissions() { Set posixFilePermissions = null; try { - posixFilePermissions = Files.readAttributes(resourcePath, PosixFileAttributes.class) - .permissions(); + if (attributes instanceof PosixFileAttributes) { + posixFilePermissions = ((PosixFileAttributes) attributes).permissions(); + } else { + posixFilePermissions = Files.readAttributes(resourcePath, PosixFileAttributes.class) + .permissions(); + } } catch (UnsupportedOperationException | IOException | SecurityException e) { // POSIX attributes not supported } @@ -297,7 +311,7 @@ private void markAsOpen() throws IOException { @Override Resource getResource(final String relativePath) { final Path resourcePath = classpathEltPath.resolve(relativePath); - return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, nestedJarHandler) : null; + return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, null) : null; } /** @@ -393,6 +407,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } Collections.sort(pathsInDir); + Function getFileAttributes = createAttributeCache(pathsInDir); // Determine whether this is a modular jar running under JRE 9+ final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; @@ -400,9 +415,11 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Only scan files in directory if directory is not only an ancestor of an accepted path if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH) { // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses - for (final Path subPath : pathsInDir) { + for (final Path subPath : List.copyOf(pathsInDir)) { // Process files in dir before recursing - if (Files.isRegularFile(subPath)) { + BasicFileAttributes fileAttributes = getFileAttributes.apply(subPath); + if (fileAttributes.isRegularFile()) { + pathsInDir.remove(subPath); final Path subPathRelative = classpathEltPath.relativize(subPath); final String subPathRelativeStr = FastPathResolver.resolve(subPathRelative.toString()); // If this is a modular jar, ignore all classfiles other than "module-info.class" in the @@ -423,12 +440,12 @@ private void scanPathRecursively(final Path path, final LogNode log) { || (parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_CLASS_PACKAGE && scanSpec.classfileIsSpecificallyAccepted(subPathRelativeStr))) { // Resource is accepted - final Resource resource = newResource(subPath, nestedJarHandler); + final Resource resource = newResource(subPath, fileAttributes); addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time try { - fileToLastModified.put(subPath.toFile(), subPath.toFile().lastModified()); + fileToLastModified.put(subPath.toFile(), fileAttributes.lastModifiedTime().toMillis()); } catch (final UnsupportedOperationException e) { // Ignore } @@ -441,23 +458,27 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { // Always check for module descriptor in package root, even if package root isn't in accept - for (final Path subPath : pathsInDir) { - if (subPath.getFileName().toString().equals("module-info.class") && Files.isRegularFile(subPath)) { - final Resource resource = newResource(subPath, nestedJarHandler); - addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); - try { - fileToLastModified.put(subPath.toFile(), subPath.toFile().lastModified()); - } catch (final UnsupportedOperationException e) { - // Ignore + for (final Path subPath : List.copyOf(pathsInDir)) { + if (subPath.getFileName().toString().equals("module-info.class")) { + BasicFileAttributes fileAttributes = getFileAttributes.apply(subPath); + if (fileAttributes.isRegularFile()) { + pathsInDir.remove(subPath); + final Resource resource = newResource(subPath, fileAttributes); + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + try { + fileToLastModified.put(subPath.toFile(), fileAttributes.lastModifiedTime().toMillis()); + } catch (final UnsupportedOperationException e) { + // Ignore + } + break; } - break; } } } // Recurse into subdirectories for (final Path subPath : pathsInDir) { try { - if (Files.isDirectory(subPath)) { + if (FileUtils.isDir(subPath)) { scanPathRecursively(subPath, subLog); } } catch (final SecurityException e) { @@ -480,6 +501,62 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } + private static Function createAttributeCache(List pathsInDir) { + Map attributesCache = new HashMap<>(pathsInDir.size()); + return path -> attributesCache.computeIfAbsent(path, forPath -> { + try { + return Files.readAttributes(forPath, BasicFileAttributes.class); + } catch (IOException e) { + return new BasicFileAttributes() { + @Override + public FileTime lastModifiedTime() { + return FileTime.fromMillis(path.toFile().lastModified()); + } + + @Override + public FileTime lastAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public FileTime creationTime() { + return FileTime.fromMillis(0); + } + + @Override + public boolean isRegularFile() { + return FileUtils.isFile(path); + } + + @Override + public boolean isDirectory() { + return FileUtils.isDir(path); + } + + @Override + public boolean isSymbolicLink() { + return false; + } + + @Override + public boolean isOther() { + return !isRegularFile() && !isDirectory(); + } + + @Override + public long size() { + return path.toFile().length(); + } + + @Override + public Object fileKey() { + throw new UnsupportedOperationException(); + } + }; + } + }); + } + /** * Hierarchically scan directory structure for classfiles and matching files. * diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 162a7f860..b8168810a 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -351,6 +351,16 @@ public static boolean canReadAndIsFile(final Path path) { return Files.isRegularFile(path); } + public static boolean isFile(final Path path) { + try { + return path.toFile().isFile(); + } catch (UnsupportedOperationException e) { + return Files.isRegularFile(path); + } catch (SecurityException e) { + return false; + } + } + /** * Check if a {@link File} exists, is a regular file, and can be read. * @@ -438,6 +448,16 @@ public static boolean canReadAndIsDir(final Path path) { return Files.isDirectory(path); } + public static boolean isDir(final Path path) { + try { + return path.toFile().isDirectory(); + } catch (UnsupportedOperationException e) { + return Files.isDirectory(path); + } catch (SecurityException e) { + return false; + } + } + /** * Check if a {@link File} exists, is a directory, and can be read. * From 5a79eb7f4fa92aa42bc9e66df588d5f1d8d7d9ff Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Wed, 17 Apr 2024 08:51:57 +0200 Subject: [PATCH 639/713] Restore JRE 7 compatibility The previous commits introduced some code that would not work on JRE 7, which are fixed here. --- .../classgraph/ClasspathElementDir.java | 72 ++---------------- .../io/github/classgraph/utils/FileUtils.java | 76 +++++++++++++++++++ 2 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 2be0cb235..14ee45fa5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -38,19 +38,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Function; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; @@ -163,7 +159,7 @@ void open(final WorkQueue workQueue, final LogNode log) * the {@link Path} for the resource * @return the resource */ - private Resource newResource(final Path resourcePath, BasicFileAttributes attributes) { + private Resource newResource(final Path resourcePath, final BasicFileAttributes attributes) { final int notYetLoadedLength = -2; return new Resource(this, attributes == null ? notYetLoadedLength : attributes.size()) { /** The {@link PathSlice} opened on the file. */ @@ -407,7 +403,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } Collections.sort(pathsInDir); - Function getFileAttributes = createAttributeCache(pathsInDir); + FileUtils.FileAttributesGetter getFileAttributes = FileUtils.createCachedAttributesGetter(); // Determine whether this is a modular jar running under JRE 9+ final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; @@ -415,9 +411,9 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Only scan files in directory if directory is not only an ancestor of an accepted path if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH) { // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses - for (final Path subPath : List.copyOf(pathsInDir)) { + for (final Path subPath : new ArrayList<>(pathsInDir)) { // Process files in dir before recursing - BasicFileAttributes fileAttributes = getFileAttributes.apply(subPath); + BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { pathsInDir.remove(subPath); final Path subPathRelative = classpathEltPath.relativize(subPath); @@ -458,9 +454,9 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { // Always check for module descriptor in package root, even if package root isn't in accept - for (final Path subPath : List.copyOf(pathsInDir)) { + for (final Path subPath : new ArrayList<>(pathsInDir)) { if (subPath.getFileName().toString().equals("module-info.class")) { - BasicFileAttributes fileAttributes = getFileAttributes.apply(subPath); + BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { pathsInDir.remove(subPath); final Resource resource = newResource(subPath, fileAttributes); @@ -501,62 +497,6 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } - private static Function createAttributeCache(List pathsInDir) { - Map attributesCache = new HashMap<>(pathsInDir.size()); - return path -> attributesCache.computeIfAbsent(path, forPath -> { - try { - return Files.readAttributes(forPath, BasicFileAttributes.class); - } catch (IOException e) { - return new BasicFileAttributes() { - @Override - public FileTime lastModifiedTime() { - return FileTime.fromMillis(path.toFile().lastModified()); - } - - @Override - public FileTime lastAccessTime() { - throw new UnsupportedOperationException(); - } - - @Override - public FileTime creationTime() { - return FileTime.fromMillis(0); - } - - @Override - public boolean isRegularFile() { - return FileUtils.isFile(path); - } - - @Override - public boolean isDirectory() { - return FileUtils.isDir(path); - } - - @Override - public boolean isSymbolicLink() { - return false; - } - - @Override - public boolean isOther() { - return !isRegularFile() && !isDirectory(); - } - - @Override - public long size() { - return path.toFile().length(); - } - - @Override - public Object fileKey() { - throw new UnsupportedOperationException(); - } - }; - } - }); - } - /** * Hierarchically scan directory structure for classfiles and matching files. * diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index b8168810a..3ca7fc732 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -39,8 +39,12 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; @@ -729,4 +733,76 @@ public Boolean call() throws Exception { return false; } } + + public static FileAttributesGetter createCachedAttributesGetter() { + final Map cache = new HashMap<>(); + return new FileAttributesGetter() { + @Override + public BasicFileAttributes get(final Path path) { + BasicFileAttributes attributes = cache.get(path); + if (attributes == null) { + attributes = readAttributes(path); + cache.put(path, attributes); + } + return attributes; + } + }; + } + + public static BasicFileAttributes readAttributes(final Path path) { + try { + return Files.readAttributes(path, BasicFileAttributes.class); + } catch (IOException e) { + return new BasicFileAttributes() { + @Override + public FileTime lastModifiedTime() { + return FileTime.fromMillis(path.toFile().lastModified()); + } + + @Override + public FileTime lastAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public FileTime creationTime() { + return FileTime.fromMillis(0); + } + + @Override + public boolean isRegularFile() { + return FileUtils.isFile(path); + } + + @Override + public boolean isDirectory() { + return FileUtils.isDir(path); + } + + @Override + public boolean isSymbolicLink() { + return false; + } + + @Override + public boolean isOther() { + return !isRegularFile() && !isDirectory(); + } + + @Override + public long size() { + return path.toFile().length(); + } + + @Override + public Object fileKey() { + throw new UnsupportedOperationException(); + } + }; + } + } + + public interface FileAttributesGetter { + BasicFileAttributes get(Path path); + } } From 6fb124d5744d4b93d645bdb2de1ed9278efde033 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Wed, 17 Apr 2024 14:39:27 +0200 Subject: [PATCH 640/713] Optimize filtering of nested jar files Flip the condition check to first check the fileName since it is cheaper and also use the built-in filtering. The latter does not really contribute to the performance benefit though. --- .../classgraph/ClasspathElementDir.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 14ee45fa5..5c1058896 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -111,17 +111,20 @@ void open(final WorkQueue workQueue, final LogNode log) final Path libDirPath = classpathEltPath.resolve(libDirPrefix); if (FileUtils.canReadAndIsDir(libDirPath)) { // Add all jarfiles within the lib dir as child classpath entries - try (DirectoryStream stream = Files.newDirectoryStream(libDirPath)) { + try (DirectoryStream stream = Files.newDirectoryStream(libDirPath, new DirectoryStream.Filter() { + @Override + public boolean accept(Path filePath) { + return filePath.toString().toLowerCase().endsWith(".jar") && Files.isRegularFile(filePath); + } + })) { for (final Path filePath : stream) { - if (Files.isRegularFile(filePath) && filePath.toString().toLowerCase().endsWith(".jar")) { - if (log != null) { - log(classpathElementIdx, "Found lib jar: " + filePath, log); - } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit(filePath, getClassLoader(), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, - /* packageRootPrefix = */ "")); + if (log != null) { + log(classpathElementIdx, "Found lib jar: " + filePath, log); } + workQueue.addWorkUnit(new ClasspathEntryWorkUnit(filePath, getClassLoader(), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++, + /* packageRootPrefix = */ "")); } } catch (final IOException e) { // Ignore -- thrown by Files.newDirectoryStream From dac132162d9dece539f86380f24bbd4f945f7d94 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Wed, 17 Apr 2024 13:42:35 +0200 Subject: [PATCH 641/713] Avoid double checking of paths in ClasspathElementDir ClasspathElementDir already checks whether the resource can be accessed and is a file before creating it. So, in order to avoid the redundant file access we do not check the same in PathSlice created via these Resources. --- .../classgraph/ClasspathElementDir.java | 27 +++++++---------- .../classgraph/fileslice/PathSlice.java | 29 +++++++++++++++++-- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 5c1058896..f5f1c9fef 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -226,9 +226,7 @@ public Set getPosixFilePermissions() { @Override public ByteBuffer read() throws IOException { checkSkipState(); - markAsOpen(); - pathSlice = new PathSlice(resourcePath, nestedJarHandler); - length = pathSlice.sliceLength; + openAndCreateSlice(); byteBuffer = pathSlice.read(); return byteBuffer; } @@ -236,32 +234,27 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { checkSkipState(); - markAsOpen(); // Classfile won't be compressed, so wrap it in a new PathSlice and then open it - pathSlice = new PathSlice(resourcePath, nestedJarHandler); - length = pathSlice.sliceLength; + openAndCreateSlice(); return new ClassfileReader(pathSlice, this); } @Override public InputStream open() throws IOException { checkSkipState(); - markAsOpen(); - pathSlice = new PathSlice(resourcePath, nestedJarHandler); + openAndCreateSlice(); inputStream = pathSlice.open(this); - length = pathSlice.sliceLength; return inputStream; } @Override public byte[] load() throws IOException { checkSkipState(); - markAsOpen(); - try (Resource res = this) { // Close this after use - pathSlice = new PathSlice(resourcePath, nestedJarHandler); - final byte[] bytes = pathSlice.load(); - res.length = bytes.length; - return bytes; + try { + openAndCreateSlice(); + return pathSlice.load(); + } finally { + close(); } } @@ -290,11 +283,13 @@ private void checkSkipState() throws IOException { } } - private void markAsOpen() throws IOException { + private void openAndCreateSlice() throws IOException { if (isOpen.getAndSet(true)) { throw new IOException( "Resource is already open -- cannot open it again without first calling close()"); } + pathSlice = new PathSlice(resourcePath, false, 0L, nestedJarHandler, false); + length = pathSlice.sliceLength; } }; } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java index b421332b1..07fd0f37f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -112,10 +112,35 @@ private PathSlice(final PathSlice parentSlice, final long offset, final long len */ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) throws IOException { + this(path, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler, true); + } + + /** + * Constructor for toplevel file slice. + * + * @param path + * the path + * @param isDeflatedZipEntry + * true if this is a deflated zip entry + * @param inflatedLengthHint + * the uncompressed size of a deflated zip entry, or + * -1 if unknown, or 0 of this is not a deflated + * zip entry. + * @param nestedJarHandler + * the nested jar handler + * @param checkAccess + * whether it is needed to check read access and if it is a file + * @throws IOException + * if the file cannot be opened. + */ + public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler, boolean checkAccess) throws IOException { super(0L, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); - // Make sure the File is readable and is a regular file - FileUtils.checkCanReadAndIsFile(path); + if (checkAccess) { + // Make sure the File is readable and is a regular file + FileUtils.checkCanReadAndIsFile(path); + } this.path = path; this.fileChannel = FileChannel.open(path, StandardOpenOption.READ); From a81b80e6d889e083c71ccdecfa1e19d7037d2750 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 17 Apr 2024 12:46:22 -0600 Subject: [PATCH 642/713] [maven-release-plugin] prepare release classgraph-4.8.171 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5d7dd9167..32de883fe 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.171-SNAPSHOT + 4.8.171 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.167 + classgraph-4.8.171 From 9ea78719c1dbf5bdca9e9756c533966966e2a545 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 17 Apr 2024 12:46:25 -0600 Subject: [PATCH 643/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 32de883fe..2165352eb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.171 + 4.8.172-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.171 + classgraph-4.8.167 From 9bf48dc8137817a944247e1fbe9923ee8298c3e7 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Fri, 19 Apr 2024 09:04:22 +0200 Subject: [PATCH 644/713] Use file attributes cache when checking subdirectories The file attributes at this point have already been requested in order to check whether the subPath is a regular file and so there is no need for an additional isDir call. --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index f5f1c9fef..42e4c674f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -472,7 +472,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Recurse into subdirectories for (final Path subPath : pathsInDir) { try { - if (FileUtils.isDir(subPath)) { + if (getFileAttributes.get(subPath).isDirectory()) { scanPathRecursively(subPath, subLog); } } catch (final SecurityException e) { From 3d2ebc2fa6300dfe3b3c73cfd7c1bf5feab0aa89 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Fri, 19 Apr 2024 09:08:24 +0200 Subject: [PATCH 645/713] Use Iterator.remove instead of List copy To point is that when we already know from some paths to be just regular files, but not directories then at the end we do not need to check if these are directories. But rather than copying the list we can use iterators to remove the already discovered files. --- .../io/github/classgraph/ClasspathElementDir.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 42e4c674f..20ab58251 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -409,11 +410,13 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Only scan files in directory if directory is not only an ancestor of an accepted path if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH) { // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses - for (final Path subPath : new ArrayList<>(pathsInDir)) { + final Iterator pathsIterator = pathsInDir.iterator(); + while (pathsIterator.hasNext()) { + final Path subPath = pathsIterator.next(); // Process files in dir before recursing BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { - pathsInDir.remove(subPath); + pathsIterator.remove(); final Path subPathRelative = classpathEltPath.relativize(subPath); final String subPathRelativeStr = FastPathResolver.resolve(subPathRelative.toString()); // If this is a modular jar, ignore all classfiles other than "module-info.class" in the @@ -452,11 +455,13 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { // Always check for module descriptor in package root, even if package root isn't in accept - for (final Path subPath : new ArrayList<>(pathsInDir)) { + final Iterator pathsIterator = pathsInDir.iterator(); + while (pathsIterator.hasNext()) { + final Path subPath = pathsIterator.next(); if (subPath.getFileName().toString().equals("module-info.class")) { BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { - pathsInDir.remove(subPath); + pathsIterator.remove(); final Resource resource = newResource(subPath, fileAttributes); addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); try { From d3b229538bb7b9badef53307ce238377ade3c0c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 19 Apr 2024 10:50:21 -0600 Subject: [PATCH 646/713] [maven-release-plugin] prepare release classgraph-4.8.172 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2165352eb..9731e8b60 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.172-SNAPSHOT + 4.8.172 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.167 + classgraph-4.8.172 From cb447645be7365d1c213f305b37f0ad80326b4bc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 19 Apr 2024 10:50:24 -0600 Subject: [PATCH 647/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9731e8b60..d3f08cdbd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.172 + 4.8.173-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.172 + classgraph-4.8.167 From fd734184658c49f647405ddbc413d02fd3aa4ba8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 02:12:10 +0000 Subject: [PATCH 648/713] Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.0 to 3.4.1 Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.0...maven-jar-plugin-3.4.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..9c7fbbcb0 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.0 + 3.4.1 org.apache.maven.plugins From 9bf31a0ce30db4927e546a0f83462e1f14fd7a18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 02:12:14 +0000 Subject: [PATCH 649/713] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.3 to 3.2.4 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.3 to 3.2.4. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.3...maven-gpg-plugin-3.2.4) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..da4a0e28c 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.3 + 3.2.4 org.sonatype.plugins From d71230047dfa5fb82cf798a28ce605bae8544eaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 02:53:48 +0000 Subject: [PATCH 650/713] Bump org.apache.maven.plugins:maven-install-plugin from 3.1.1 to 3.1.2 Bumps [org.apache.maven.plugins:maven-install-plugin](https://github.com/apache/maven-install-plugin) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/apache/maven-install-plugin/releases) - [Commits](https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-3.1.1...maven-install-plugin-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-install-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..6357ea150 100644 --- a/pom.xml +++ b/pom.xml @@ -242,7 +242,7 @@ org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins From a7041444ef3e0a7be2e3681ddf480e4f608fa620 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 02:53:53 +0000 Subject: [PATCH 651/713] Bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.1 to 3.1.2 Bumps [org.apache.maven.plugins:maven-deploy-plugin](https://github.com/apache/maven-deploy-plugin) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/apache/maven-deploy-plugin/releases) - [Commits](https://github.com/apache/maven-deploy-plugin/compare/maven-deploy-plugin-3.1.1...maven-deploy-plugin-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-deploy-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..f55b8228d 100644 --- a/pom.xml +++ b/pom.xml @@ -237,7 +237,7 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins From 68475b2852d8ebc05e5f993d488d539677c767a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 02:24:24 +0000 Subject: [PATCH 652/713] Bump io.github.toolfactory:jvm-driver from 9.6.0 to 9.7.1 Bumps [io.github.toolfactory:jvm-driver](https://github.com/toolfactory/jvm-driver) from 9.6.0 to 9.7.1. - [Commits](https://github.com/toolfactory/jvm-driver/compare/jvm-driver-9.6.0...jvm-driver-9.7.1) --- updated-dependencies: - dependency-name: io.github.toolfactory:jvm-driver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..89616ce11 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ io.github.toolfactory jvm-driver - 9.6.0 + 9.7.1 true From 223fc467b84cf1d85463b9ae6570d6971de0bd45 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Jun 2024 16:52:03 -0600 Subject: [PATCH 653/713] Fix for compiler bug when scanning guava jars (#861) --- .../java/io/github/classgraph/Classfile.java | 10 ++++++ .../issues/issue854/Issue854Test.java | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 4fac237a4..ef16f3029 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1554,6 +1554,16 @@ private void readMethods() throws IOException, ClassfileFormatException { boundIndex = -1; formalParameterIndex = -1; throwsTypeIndex = -1; + } else if (targetType == 0x10) { + // This target_type is not supposed to be added to methods, it is intended + // for ClassFile annotations, but Google's Java compiler adds annotations + // of this type to methods in guava for some reason. Just ignore these + // annotations. (#861) + reader.readUnsignedShort(); + typeParameterIndex = -1; + boundIndex = -1; + formalParameterIndex = -1; + throwsTypeIndex = -1; } else if (targetType == 0x12) { // Type in bound of type parameter declaration of generic method // or constructor diff --git a/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java new file mode 100644 index 000000000..f2c9ebb57 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java @@ -0,0 +1,33 @@ +package io.github.classgraph.issues.issue854; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassRefTypeSignature; +import io.github.classgraph.ScanResult; + +import static org.junit.jupiter.api.Assertions.*; + +class Issue854Test { + @Test + void getFullyQualifiedClassName() { + ClassLoader mainClassLoader = Issue854Test.class.getClassLoader(); + ScanResult scanResult = new ClassGraph().enableClassInfo().enableAnnotationInfo().ignoreClassVisibility() + .ignoreFieldVisibility().ignoreMethodVisibility().overrideClassLoaders(mainClassLoader).scan(); + + String anonymousClass = "com.google.common.collect.TreeRangeMap$SubRangeMap$1"; + ClassInfo classInfo = scanResult.getClassInfo(anonymousClass); + ClassRefTypeSignature signature = classInfo.getTypeSignatureOrTypeDescriptor().getSuperclassSignature(); + + String subRangeMapAsMapClassName = signature.getFullyQualifiedClassName(); + // This is what we get back + assertEquals("com.google.common.collect.TreeRangeMap$SubRangeMap.SubRangeMapAsMap", + subRangeMapAsMapClassName); + // With the "$" rather than the "." it would actually be usable to get class info for + String rightClassName = "com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap"; + assertNotNull(scanResult.getClassInfo(rightClassName)); + // But unfortunately it will fail here + assertNotNull(scanResult.getClassInfo(subRangeMapAsMapClassName)); + } +} \ No newline at end of file From af7118369e6e69abc02ef95e228bdfa6f39c2fbc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Jun 2024 17:46:02 -0600 Subject: [PATCH 654/713] Allow '.' in class name suffixes (#854) --- pom.xml | 4 ++-- .../classgraph/ClassRefTypeSignature.java | 5 +++-- .../io/github/classgraph/TypeParameter.java | 2 +- .../classgraph/TypeVariableSignature.java | 2 +- .../io/github/classgraph/types/TypeUtils.java | 9 +++++--- .../issues/issue854/Issue854Test.java | 22 ++++++++++--------- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index d3f08cdbd..497a3db60 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.173-SNAPSHOT + 4.8.173 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.167 + classgraph-4.8.173 diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 35d67cd40..081d36f45 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -467,7 +467,7 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla if (parser.peek() == 'L') { parser.next(); final int startParserPosition = parser.getPosition(); - if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true, /* stopAtDot = */ true)) { throw new ParseException(parser, "Could not parse identifier token"); } String className = parser.currToken(); @@ -480,7 +480,8 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla suffixTypeArguments = new ArrayList<>(); while (parser.peek() == '.' || parser.peek() == '$') { parser.advance(1); - if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true, + /* stopAtDot = */ true)) { // Got the empty string as the next token after '$', i.e. found an empty suffix. suffixes.add(""); suffixTypeArguments.add(Collections. emptyList()); diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index e05936234..308e4d039 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -131,7 +131,7 @@ static List parseList(final Parser parser, final String definingC throw new ParseException(parser, "Missing '>'"); } // Scala can contain '$' in type parameter names (#495) - if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false, /* stopAtDot = */ true)) { throw new ParseException(parser, "Could not parse identifier token"); } final String identifier = parser.currToken(); diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 0f1cfdd8c..722428c72 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -164,7 +164,7 @@ static TypeVariableSignature parse(final Parser parser, final String definingCla if (peek == 'T') { parser.next(); // Scala can contain '$' in type variable names (#495) - if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false, /* stopAtDot = */ true)) { throw new ParseException(parser, "Could not parse type variable signature"); } parser.expect(';'); diff --git a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java index bb8385303..7dd48db8a 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -51,9 +51,12 @@ private TypeUtils() { * The parser. * @param stopAtDollarSign * If true, stop parsing when the first '$' is hit. + * @param stopAtDot + * If true, stop parsing when the first '.' is hit. * @return true if at least one identifier character was parsed. */ - public static boolean getIdentifierToken(final Parser parser, final boolean stopAtDollarSign) { + public static boolean getIdentifierToken(final Parser parser, final boolean stopAtDollarSign, + final boolean stopAtDot) { boolean consumedChar = false; while (parser.hasMore()) { final char c = parser.peek(); @@ -61,8 +64,8 @@ public static boolean getIdentifierToken(final Parser parser, final boolean stop parser.appendToToken('.'); parser.next(); consumedChar = true; - } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' - && (!stopAtDollarSign || c != '$')) { + } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' && (!stopAtDollarSign || c != '$') + && (!stopAtDot || c != '.')) { parser.appendToToken(c); parser.next(); consumedChar = true; diff --git a/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java index f2c9ebb57..1a61ff981 100644 --- a/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java +++ b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java @@ -1,5 +1,8 @@ package io.github.classgraph.issues.issue854; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -7,27 +10,26 @@ import io.github.classgraph.ClassRefTypeSignature; import io.github.classgraph.ScanResult; -import static org.junit.jupiter.api.Assertions.*; - class Issue854Test { @Test void getFullyQualifiedClassName() { ClassLoader mainClassLoader = Issue854Test.class.getClassLoader(); ScanResult scanResult = new ClassGraph().enableClassInfo().enableAnnotationInfo().ignoreClassVisibility() - .ignoreFieldVisibility().ignoreMethodVisibility().overrideClassLoaders(mainClassLoader).scan(); + .ignoreFieldVisibility().ignoreMethodVisibility().overrideClassLoaders(mainClassLoader) + .acceptPackages("com.google.common.collect").scan(); String anonymousClass = "com.google.common.collect.TreeRangeMap$SubRangeMap$1"; ClassInfo classInfo = scanResult.getClassInfo(anonymousClass); ClassRefTypeSignature signature = classInfo.getTypeSignatureOrTypeDescriptor().getSuperclassSignature(); + // Before the fix to 854, this would give the following, because type parameter token parsing + // did not stop at '.': + // com.google.common.collect.TreeRangeMap$SubRangeMap.SubRangeMapAsMap + // But the fully-qualified class name in the classfile is: + // com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap String subRangeMapAsMapClassName = signature.getFullyQualifiedClassName(); - // This is what we get back - assertEquals("com.google.common.collect.TreeRangeMap$SubRangeMap.SubRangeMapAsMap", - subRangeMapAsMapClassName); - // With the "$" rather than the "." it would actually be usable to get class info for - String rightClassName = "com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap"; - assertNotNull(scanResult.getClassInfo(rightClassName)); - // But unfortunately it will fail here + assertThat(subRangeMapAsMapClassName) + .isEqualTo("com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap"); assertNotNull(scanResult.getClassInfo(subRangeMapAsMapClassName)); } } \ No newline at end of file From 133020fd3306521c2fa4ef409547d7ade0e70475 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Jun 2024 17:48:24 -0600 Subject: [PATCH 655/713] Move version number back --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 05637c821..969b8480a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.173 + 4.8.173-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6bace94f939ec75ffdd15d9e157090ec3444b6a9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Jun 2024 17:56:43 -0600 Subject: [PATCH 656/713] Bump dep versions back down --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 969b8480a..79494ade8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.173-SNAPSHOT + 4.8.173 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -195,7 +195,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.1 + 3.4.0 org.apache.maven.plugins @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.3 org.sonatype.plugins @@ -237,12 +237,12 @@ org.apache.maven.plugins maven-deploy-plugin - 3.1.2 + 3.1.1 org.apache.maven.plugins maven-install-plugin - 3.1.2 + 3.1.1 org.apache.maven.plugins From 9b62ebc0a121ba320d641461ee56e496925fb90e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Jun 2024 17:57:03 -0600 Subject: [PATCH 657/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79494ade8..29c3e536b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.173 + 4.8.174-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 23f02debd665fa502d7cdc1858587fee5a3c1535 Mon Sep 17 00:00:00 2001 From: dominik2611 Date: Tue, 11 Jun 2024 13:43:34 +0200 Subject: [PATCH 658/713] adjusted JBossClassLoaderHandler to changed VFS of JBoss/Wildfly (https://github.com/classgraph/classgraph/issues/843) --- .../JBossClassLoaderHandler.java | 137 +++++++++++++++--- 1 file changed, 118 insertions(+), 19 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 439d315b2..9347d0d07 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -30,7 +30,9 @@ import java.io.File; import java.lang.reflect.Array; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -79,7 +81,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * the log */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, - final LogNode log) { + final LogNode log) { classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); classLoaderOrder.add(classLoader, log); } @@ -99,12 +101,122 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * the log */ private static void handleResourceLoader(final Object resourceLoader, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { + final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { if (resourceLoader == null) { return; } // PathResourceLoader has root field, which is a Path object final Object root = classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "root"); + + String path = loadJarPathFromClassicVFS(root, classpathOrderOut); + if(!isPathExisting(path)) { + path = loadJarPathFromNewVFS(root, classpathOrderOut, classLoader); + } + + if (path == null) { + final File file = (File) classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, + "fileOfJar"); + if (file != null) { + path = file.getAbsolutePath(); + } + } + if (path != null) { + classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); + } else { + if (log != null) { + log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); + } + } + } + + /** + * Checks if the given path exists and is a regular file. + * + * @param path the path to check + * @return true if the path exists and is a regular file, false otherwise + */ + private static boolean isPathExisting(String path) { + if(path != null && !path.isEmpty()) { + Path possibleExistingPath = Paths.get(path); + return Files.exists(possibleExistingPath) && Files.isRegularFile(possibleExistingPath); + } + return false; + } + + /** + * Returns the absolute path of a JAR file from a given root object using the JBoss VFS mechanism. + * This works for Versions of JBoss/Wildfly that contain the following change: + * WFLY-18544 + * JBEAP-25879 + * JBEAP-25677 + * @param root The root object to get the JAR path from. + * @param classpathOrderOut The ClasspathOrder object for updating the classpath order. + * @param classLoader The ClassLoader to use for loading the VFS class. Used as fallback if the current threads classloader + * does not have any dependency on jboss.vfs + * @return The absolute path of the JAR file, or null if the path couldn't be found. + */ + private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut, final ClassLoader classLoader) { + Class jbossVFS = getJBossVFSAccess(classLoader); + if (root == null || jbossVFS == null) return null; + + // try to find the mount of the root. Type is org.jboss.vfs.VFS.Mount + Object mount = classpathOrderOut.reflectionUtils.invokeStaticMethod(false, jbossVFS, "getMount", + root.getClass(), root); + if (mount == null) return null; + + // try to access the fileSystem of the mount. Type is org.jboss.vfs.spi.FileSystem + Object fileSystem = classpathOrderOut.reflectionUtils.invokeMethod(false, mount, "getFileSystem"); + if (fileSystem == null) return null; + + // now access the mount source, which is the file that is used to create the mount. + File mountSource = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, fileSystem, + "getMountSource"); + if (mountSource == null) return null; + + // absolute path of the mountSource should be the 'physical' .jar + return mountSource.getAbsolutePath(); + } + + /** + * Get the access to the JBoss VFS class. Tries to load VFS first from the current threads classloader. + * If VFS can not be found in there, the provided classloader will be used to load VFS from. + * + * @param classLoader The ClassLoader to use for loading the JBoss VFS class. + * @return The Class object representing the JBoss VFS class, or null if it couldn't be found. + */ + private static Class getJBossVFSAccess(ClassLoader classLoader) { + Class jbossVFS = null; + // we need access to the class 'VFS' of org.jboss.vfs + try { + // try to load JBoss VFS access from the current threads classloader + ClassLoader currentThreadClassLoarder = Thread.currentThread().getContextClassLoader(); + jbossVFS = Class.forName("org.jboss.vfs.VFS", true, currentThreadClassLoarder); + } catch (ClassNotFoundException ex) { + // if the classloader before could not provide the VFS, try it in the provided class loader + // NOTE: maybe we can skip this, org.jboss.vfs is less likely to be in every class loader present. + // It's easier to ensure that it is in the current threads classloader where classgraph is called from? + try { + jbossVFS = Class.forName("org.jboss.vfs.VFS", true, classLoader); + } catch (ClassNotFoundException e) { + // swallow the exception. If there is no VFS present, we can't do anything... + e.printStackTrace(); + } + } + return jbossVFS; + } + + /** + * Returns the absolute path of a JAR file from a given root object using the 'classic' VFS read mechanism. + * This works for Versions of JBoss/Wildfly prior to this change: + * WFLY-18544 + * JBEAP-25879 + * JBEAP-25677 + * @param root The root object to get the JAR path from. + * @param classpathOrderOut The ClasspathOrder object for updating the classpath order. + * @return The absolute path of the JAR file, or null if the path couldn't be found. + */ + private static String loadJarPathFromClassicVFS(final Object root, final ClasspathOrder classpathOrderOut) { + // type VirtualFile final File physicalFile = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPhysicalFile"); @@ -134,20 +246,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas } } } - if (path == null) { - final File file = (File) classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, - "fileOfJar"); - if (file != null) { - path = file.getAbsolutePath(); - } - } - if (path != null) { - classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); - } else { - if (log != null) { - log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); - } - } + return path; } /** @@ -167,8 +266,8 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas * the log */ private static void handleRealModule(final Object module, final Set visitedModules, - final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, - final LogNode log) { + final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, + final LogNode log) { if (!visitedModules.add(module)) { // Avoid extracting paths from the same module more than once return; @@ -209,7 +308,7 @@ private static void handleRealModule(final Object module, final Set visi * the log. */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, - final ScanSpec scanSpec, final LogNode log) { + final ScanSpec scanSpec, final LogNode log) { final Object module = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getModule"); final Object callerModuleLoader = classpathOrder.reflectionUtils.invokeMethod(false, module, "getCallerModuleLoader"); From c419fd57f1c8403f2d7aea407c16fb94bf472fa1 Mon Sep 17 00:00:00 2001 From: dominik2611 Date: Wed, 12 Jun 2024 14:21:54 +0200 Subject: [PATCH 659/713] adjustments in getting access to org.jboss.vfs.VFS (https://github.com/classgraph/classgraph/issues/843) --- .../JBossClassLoaderHandler.java | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 9347d0d07..f857d464a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -39,7 +39,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -110,7 +109,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas String path = loadJarPathFromClassicVFS(root, classpathOrderOut); if(!isPathExisting(path)) { - path = loadJarPathFromNewVFS(root, classpathOrderOut, classLoader); + path = loadJarPathFromNewVFS(root, classpathOrderOut); } if (path == null) { @@ -151,13 +150,14 @@ private static boolean isPathExisting(String path) { * JBEAP-25677 * @param root The root object to get the JAR path from. * @param classpathOrderOut The ClasspathOrder object for updating the classpath order. - * @param classLoader The ClassLoader to use for loading the VFS class. Used as fallback if the current threads classloader - * does not have any dependency on jboss.vfs * @return The absolute path of the JAR file, or null if the path couldn't be found. */ - private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut, final ClassLoader classLoader) { - Class jbossVFS = getJBossVFSAccess(classLoader); - if (root == null || jbossVFS == null) return null; + private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut) { + + if (root == null) return null; + + Class jbossVFS = getJBossVFSAccess(root); + if(jbossVFS == null) return null; // try to find the mount of the root. Type is org.jboss.vfs.VFS.Mount Object mount = classpathOrderOut.reflectionUtils.invokeStaticMethod(false, jbossVFS, "getMount", @@ -178,33 +178,45 @@ private static String loadJarPathFromNewVFS(final Object root, final ClasspathOr } /** - * Get the access to the JBoss VFS class. Tries to load VFS first from the current threads classloader. - * If VFS can not be found in there, the provided classloader will be used to load VFS from. + * Get the access to the JBoss VFS class. Tries to load VFS first from the classloader of the provided root object + * if it's an object from org.jboss.vfs. + * If the root object is not from org.jboss.vfs, VFS will be tried to be loaded from the current thread class loader. + * It might be unnecessary to load VFS from the current thread context, because this means that the root object + * is not from org.jboss.vfs and VFS will not help here... but as a defensive approach we really try to get VFS + * access here. * - * @param classLoader The ClassLoader to use for loading the JBoss VFS class. + * @param root The root VirtualFile of JBoss VFS. Used to load the VFS via the classloader of the root. Can not be null. * @return The Class object representing the JBoss VFS class, or null if it couldn't be found. */ - private static Class getJBossVFSAccess(ClassLoader classLoader) { + private static Class getJBossVFSAccess( final Object root) { Class jbossVFS = null; // we need access to the class 'VFS' of org.jboss.vfs try { - // try to load JBoss VFS access from the current threads classloader - ClassLoader currentThreadClassLoarder = Thread.currentThread().getContextClassLoader(); - jbossVFS = Class.forName("org.jboss.vfs.VFS", true, currentThreadClassLoarder); - } catch (ClassNotFoundException ex) { - // if the classloader before could not provide the VFS, try it in the provided class loader - // NOTE: maybe we can skip this, org.jboss.vfs is less likely to be in every class loader present. - // It's easier to ensure that it is in the current threads classloader where classgraph is called from? + if(root.getClass().getName().contains("org.jboss.vfs")) { + // first, try the classloader of the root object. Since the root object comes from org.jboss.vfs, + // it is likely that we can get access to org.jboss.vfs.VFS from this classloader + ClassLoader vfsRootClassloader= root.getClass().getClassLoader(); + jbossVFS = loadJBossVFS(vfsRootClassloader); + }else { + // for non org.jboss.vfs objects, use the currentThread + jbossVFS = loadJBossVFS(Thread.currentThread().getContextClassLoader()); + } + } catch (ClassNotFoundException e) { try { - jbossVFS = Class.forName("org.jboss.vfs.VFS", true, classLoader); - } catch (ClassNotFoundException e) { + // try to load JBoss VFS access from the current threads classloader since the previous method failed + // if the previous method was already the currentThreads classloader, it will fail again... + jbossVFS = loadJBossVFS(Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e1) { // swallow the exception. If there is no VFS present, we can't do anything... - e.printStackTrace(); } } return jbossVFS; } + private static Class loadJBossVFS(ClassLoader classLoader) throws ClassNotFoundException { + return Class.forName("org.jboss.vfs.VFS", true, classLoader); + } + /** * Returns the absolute path of a JAR file from a given root object using the 'classic' VFS read mechanism. * This works for Versions of JBoss/Wildfly prior to this change: @@ -216,11 +228,10 @@ private static Class getJBossVFSAccess(ClassLoader classLoader) { * @return The absolute path of the JAR file, or null if the path couldn't be found. */ private static String loadJarPathFromClassicVFS(final Object root, final ClasspathOrder classpathOrderOut) { - + String path = null; // type VirtualFile final File physicalFile = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPhysicalFile"); - String path = null; if (physicalFile != null) { final String name = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getName"); if (name != null) { @@ -246,6 +257,7 @@ private static String loadJarPathFromClassicVFS(final Object root, final Classpa } } } + return path; } From 3e6ead2ed28ebb77510f29e624728a63473274dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 18 Jun 2024 14:02:15 -0600 Subject: [PATCH 660/713] Source > Cleanup --- .../classgraph/ClasspathElementDir.java | 23 ++-- .../java/io/github/classgraph/Scanner.java | 5 +- .../JBossClassLoaderHandler.java | 107 +++++++++++------- .../classgraph/classpath/ClasspathFinder.java | 4 +- .../classgraph/fileslice/PathSlice.java | 69 ++++++----- .../io/github/classgraph/utils/FileUtils.java | 18 +-- .../issues/issue854/Issue854Test.java | 17 +-- 7 files changed, 130 insertions(+), 113 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 20ab58251..8a6df87b4 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -112,12 +112,14 @@ void open(final WorkQueue workQueue, final LogNode log) final Path libDirPath = classpathEltPath.resolve(libDirPrefix); if (FileUtils.canReadAndIsDir(libDirPath)) { // Add all jarfiles within the lib dir as child classpath entries - try (DirectoryStream stream = Files.newDirectoryStream(libDirPath, new DirectoryStream.Filter() { - @Override - public boolean accept(Path filePath) { - return filePath.toString().toLowerCase().endsWith(".jar") && Files.isRegularFile(filePath); - } - })) { + try (DirectoryStream stream = Files.newDirectoryStream(libDirPath, + new DirectoryStream.Filter() { + @Override + public boolean accept(Path filePath) { + return filePath.toString().toLowerCase().endsWith(".jar") + && Files.isRegularFile(filePath); + } + })) { for (final Path filePath : stream) { if (log != null) { log(classpathElementIdx, "Found lib jar: " + filePath, log); @@ -201,7 +203,8 @@ public String getPathRelativeToClasspathElement() { @Override public long getLastModified() { try { - return attributes == null ? resourcePath.toFile().lastModified() : attributes.lastModifiedTime().toMillis(); + return attributes == null ? resourcePath.toFile().lastModified() + : attributes.lastModifiedTime().toMillis(); } catch (final UnsupportedOperationException e) { return 0L; } @@ -402,7 +405,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } Collections.sort(pathsInDir); - FileUtils.FileAttributesGetter getFileAttributes = FileUtils.createCachedAttributesGetter(); + final FileUtils.FileAttributesGetter getFileAttributes = FileUtils.createCachedAttributesGetter(); // Determine whether this is a modular jar running under JRE 9+ final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; @@ -414,7 +417,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { while (pathsIterator.hasNext()) { final Path subPath = pathsIterator.next(); // Process files in dir before recursing - BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); + final BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { pathsIterator.remove(); final Path subPathRelative = classpathEltPath.relativize(subPath); @@ -459,7 +462,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { while (pathsIterator.hasNext()) { final Path subPath = pathsIterator.next(); if (subPath.getFileName().toString().equals("module-info.class")) { - BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); + final BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { pathsIterator.remove(); final Resource resource = newResource(subPath, fileAttributes); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 51d0959bc..b0a8dd5df 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -34,9 +34,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -555,7 +553,8 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, if (!FileUtils.canRead(path)) { throw new IOException("Cannot read path: " + path); } else { - BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class); + final BasicFileAttributes attributes = Files.readAttributes(path, + BasicFileAttributes.class); if (attributes.isRegularFile()) { // classpathEntObj is a Path which points to a file, so it must be a jar isJar = true; diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index f857d464a..4a7f6c6ba 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; + import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -80,7 +81,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l * the log */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, - final LogNode log) { + final LogNode log) { classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); classLoaderOrder.add(classLoader, log); } @@ -100,7 +101,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * the log */ private static void handleResourceLoader(final Object resourceLoader, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { + final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { if (resourceLoader == null) { return; } @@ -108,7 +109,7 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas final Object root = classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "root"); String path = loadJarPathFromClassicVFS(root, classpathOrderOut); - if(!isPathExisting(path)) { + if (!isPathExisting(path)) { path = loadJarPathFromNewVFS(root, classpathOrderOut); } @@ -131,100 +132,118 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas /** * Checks if the given path exists and is a regular file. * - * @param path the path to check + * @param path + * the path to check * @return true if the path exists and is a regular file, false otherwise */ - private static boolean isPathExisting(String path) { - if(path != null && !path.isEmpty()) { - Path possibleExistingPath = Paths.get(path); + private static boolean isPathExisting(final String path) { + if (path != null && !path.isEmpty()) { + final Path possibleExistingPath = Paths.get(path); return Files.exists(possibleExistingPath) && Files.isRegularFile(possibleExistingPath); } return false; } /** - * Returns the absolute path of a JAR file from a given root object using the JBoss VFS mechanism. - * This works for Versions of JBoss/Wildfly that contain the following change: - * WFLY-18544 - * JBEAP-25879 - * JBEAP-25677 - * @param root The root object to get the JAR path from. - * @param classpathOrderOut The ClasspathOrder object for updating the classpath order. + * Returns the absolute path of a JAR file from a given root object using the JBoss VFS mechanism. This works + * for Versions of JBoss/Wildfly that contain the following change: + * WFLY-18544 + * JBEAP-25879 + * JBEAP-25677 + * + * @param root + * The root object to get the JAR path from. + * @param classpathOrderOut + * The ClasspathOrder object for updating the classpath order. * @return The absolute path of the JAR file, or null if the path couldn't be found. */ private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut) { - if (root == null) return null; + if (root == null) { + return null; + } - Class jbossVFS = getJBossVFSAccess(root); - if(jbossVFS == null) return null; + final Class jbossVFS = getJBossVFSAccess(root); + if (jbossVFS == null) { + return null; + } // try to find the mount of the root. Type is org.jboss.vfs.VFS.Mount - Object mount = classpathOrderOut.reflectionUtils.invokeStaticMethod(false, jbossVFS, "getMount", + final Object mount = classpathOrderOut.reflectionUtils.invokeStaticMethod(false, jbossVFS, "getMount", root.getClass(), root); - if (mount == null) return null; + if (mount == null) { + return null; + } // try to access the fileSystem of the mount. Type is org.jboss.vfs.spi.FileSystem - Object fileSystem = classpathOrderOut.reflectionUtils.invokeMethod(false, mount, "getFileSystem"); - if (fileSystem == null) return null; + final Object fileSystem = classpathOrderOut.reflectionUtils.invokeMethod(false, mount, "getFileSystem"); + if (fileSystem == null) { + return null; + } // now access the mount source, which is the file that is used to create the mount. - File mountSource = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, fileSystem, + final File mountSource = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, fileSystem, "getMountSource"); - if (mountSource == null) return null; + if (mountSource == null) { + return null; + } // absolute path of the mountSource should be the 'physical' .jar return mountSource.getAbsolutePath(); } /** - * Get the access to the JBoss VFS class. Tries to load VFS first from the classloader of the provided root object - * if it's an object from org.jboss.vfs. - * If the root object is not from org.jboss.vfs, VFS will be tried to be loaded from the current thread class loader. - * It might be unnecessary to load VFS from the current thread context, because this means that the root object - * is not from org.jboss.vfs and VFS will not help here... but as a defensive approach we really try to get VFS - * access here. + * Get the access to the JBoss VFS class. Tries to load VFS first from the classloader of the provided root + * object if it's an object from org.jboss.vfs. If the root object is not from org.jboss.vfs, VFS will be tried + * to be loaded from the current thread class loader. It might be unnecessary to load VFS from the current + * thread context, because this means that the root object is not from org.jboss.vfs and VFS will not help + * here... but as a defensive approach we really try to get VFS access here. * - * @param root The root VirtualFile of JBoss VFS. Used to load the VFS via the classloader of the root. Can not be null. + * @param root + * The root VirtualFile of JBoss VFS. Used to load the VFS via the classloader of the root. Can not + * be null. * @return The Class object representing the JBoss VFS class, or null if it couldn't be found. */ - private static Class getJBossVFSAccess( final Object root) { + private static Class getJBossVFSAccess(final Object root) { Class jbossVFS = null; // we need access to the class 'VFS' of org.jboss.vfs try { - if(root.getClass().getName().contains("org.jboss.vfs")) { + if (root.getClass().getName().contains("org.jboss.vfs")) { // first, try the classloader of the root object. Since the root object comes from org.jboss.vfs, // it is likely that we can get access to org.jboss.vfs.VFS from this classloader - ClassLoader vfsRootClassloader= root.getClass().getClassLoader(); + final ClassLoader vfsRootClassloader = root.getClass().getClassLoader(); jbossVFS = loadJBossVFS(vfsRootClassloader); - }else { + } else { // for non org.jboss.vfs objects, use the currentThread jbossVFS = loadJBossVFS(Thread.currentThread().getContextClassLoader()); } - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { try { // try to load JBoss VFS access from the current threads classloader since the previous method failed // if the previous method was already the currentThreads classloader, it will fail again... jbossVFS = loadJBossVFS(Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e1) { + } catch (final ClassNotFoundException e1) { // swallow the exception. If there is no VFS present, we can't do anything... } } return jbossVFS; } - private static Class loadJBossVFS(ClassLoader classLoader) throws ClassNotFoundException { + private static Class loadJBossVFS(final ClassLoader classLoader) throws ClassNotFoundException { return Class.forName("org.jboss.vfs.VFS", true, classLoader); } /** - * Returns the absolute path of a JAR file from a given root object using the 'classic' VFS read mechanism. - * This works for Versions of JBoss/Wildfly prior to this change: + * Returns the absolute path of a JAR file from a given root object using the 'classic' VFS read mechanism. This + * works for Versions of JBoss/Wildfly prior to this change: * WFLY-18544 * JBEAP-25879 * JBEAP-25677 - * @param root The root object to get the JAR path from. - * @param classpathOrderOut The ClasspathOrder object for updating the classpath order. + * + * @param root + * The root object to get the JAR path from. + * @param classpathOrderOut + * The ClasspathOrder object for updating the classpath order. * @return The absolute path of the JAR file, or null if the path couldn't be found. */ private static String loadJarPathFromClassicVFS(final Object root, final ClasspathOrder classpathOrderOut) { @@ -278,8 +297,8 @@ private static String loadJarPathFromClassicVFS(final Object root, final Classpa * the log */ private static void handleRealModule(final Object module, final Set visitedModules, - final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, - final LogNode log) { + final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, + final LogNode log) { if (!visitedModules.add(module)) { // Avoid extracting paths from the same module more than once return; @@ -320,7 +339,7 @@ private static void handleRealModule(final Object module, final Set visi * the log. */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, - final ScanSpec scanSpec, final LogNode log) { + final ScanSpec scanSpec, final LogNode log) { final Object module = classpathOrder.reflectionUtils.invokeMethod(false, classLoader, "getModule"); final Object callerModuleLoader = classpathOrder.reflectionUtils.invokeMethod(false, module, "getCallerModuleLoader"); diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index 189d9e102..be1e2bcc4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -136,8 +136,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection // It's not possible to instantiate AppClassLoader or PlatformClassLoader, so if these are // passed in as override classloaders, they must have been obtained using // Thread.currentThread().getContextClassLoader() [.getParent()] or similar - if (!scanSpec.enableSystemJarsAndModules && classLoaderClassName - .equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { + if (!scanSpec.enableSystemJarsAndModules + && classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { if (classpathFinderLog != null) { classpathFinderLog .log("overrideClassLoaders() was called with an instance of " + classLoaderClassName diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java index 07fd0f37f..2c490c637 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -64,19 +64,18 @@ public class PathSlice extends Slice { * Constructor for treating a range of a file as a slice. * * @param parentSlice - * the parent slice + * the parent slice * @param offset - * the offset of the sub-slice within the parent slice + * the offset of the sub-slice within the parent slice * @param length - * the length of the sub-slice + * the length of the sub-slice * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or - * -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated + * zip entry. * @param nestedJarHandler - * the nested jar handler + * the nested jar handler */ private PathSlice(final PathSlice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, @@ -98,17 +97,16 @@ private PathSlice(final PathSlice parentSlice, final long offset, final long len * Constructor for toplevel file slice. * * @param path - * the path + * the path * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or - * -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated + * zip entry. * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @throws IOException - * if the file cannot be opened. + * if the file cannot be opened. */ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) throws IOException { @@ -119,22 +117,21 @@ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long i * Constructor for toplevel file slice. * * @param path - * the path + * the path * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or - * -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated + * zip entry. * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @param checkAccess - * whether it is needed to check read access and if it is a file + * whether it is needed to check read access and if it is a file * @throws IOException - * if the file cannot be opened. + * if the file cannot be opened. */ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, - final NestedJarHandler nestedJarHandler, boolean checkAccess) throws IOException { + final NestedJarHandler nestedJarHandler, final boolean checkAccess) throws IOException { super(0L, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); if (checkAccess) { @@ -159,11 +156,11 @@ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long i * Constructor for toplevel file slice. * * @param path - * the path + * the path * @param nestedJarHandler - * the nested jar handler + * the nested jar handler * @throws IOException - * if the file cannot be opened. + * if the file cannot be opened. */ public PathSlice(final Path path, final NestedJarHandler nestedJarHandler) throws IOException { this(path, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); @@ -173,15 +170,14 @@ public PathSlice(final Path path, final NestedJarHandler nestedJarHandler) throw * Slice the file. * * @param offset - * the offset of the sub-slice within the parent slice + * the offset of the sub-slice within the parent slice * @param length - * the length of the sub-slice + * the length of the sub-slice * @param isDeflatedZipEntry - * true if this is a deflated zip entry + * true if this is a deflated zip entry * @param inflatedLengthHint - * the uncompressed size of a deflated zip entry, or - * -1 if unknown, or 0 of this is not a deflated - * zip entry. + * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated + * zip entry. * @return the slice */ @Override @@ -209,7 +205,7 @@ public RandomAccessReader randomAccessReader() { * * @return the byte[] * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ @Override public byte[] load() throws IOException { @@ -237,13 +233,12 @@ public byte[] load() throws IOException { } /** - * Read the slice into a {@link ByteBuffer} (or memory-map the slice to a - * {@link MappedByteBuffer}, if + * Read the slice into a {@link ByteBuffer} (or memory-map the slice to a {@link MappedByteBuffer}, if * {@link ClassGraph#enableMemoryMapping()} was called.) * * @return the byte buffer * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ @Override public ByteBuffer read() throws IOException { diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 3ca7fc732..26953ec1d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -306,7 +306,7 @@ public static boolean canRead(final File file) { public static boolean canRead(final Path path) { try { return canRead(path.toFile()); - } catch (UnsupportedOperationException ignored) { + } catch (final UnsupportedOperationException ignored) { } try { return Files.isReadable(path); @@ -343,7 +343,7 @@ public static boolean canReadAndIsFile(final File file) { public static boolean canReadAndIsFile(final Path path) { try { return canReadAndIsFile(path.toFile()); - } catch (UnsupportedOperationException ignored) { + } catch (final UnsupportedOperationException ignored) { } try { if (!Files.isReadable(path)) { @@ -358,9 +358,9 @@ public static boolean canReadAndIsFile(final Path path) { public static boolean isFile(final Path path) { try { return path.toFile().isFile(); - } catch (UnsupportedOperationException e) { + } catch (final UnsupportedOperationException e) { return Files.isRegularFile(path); - } catch (SecurityException e) { + } catch (final SecurityException e) { return false; } } @@ -398,7 +398,7 @@ public static void checkCanReadAndIsFile(final Path path) throws IOException { try { checkCanReadAndIsFile(path.toFile()); return; - } catch (UnsupportedOperationException ignored) { + } catch (final UnsupportedOperationException ignored) { } try { if (!Files.isReadable(path)) { @@ -440,7 +440,7 @@ public static boolean canReadAndIsDir(final File file) { public static boolean canReadAndIsDir(final Path path) { try { return canReadAndIsDir(path.toFile()); - } catch (UnsupportedOperationException ignored) { + } catch (final UnsupportedOperationException ignored) { } try { if (!Files.isReadable(path)) { @@ -455,9 +455,9 @@ public static boolean canReadAndIsDir(final Path path) { public static boolean isDir(final Path path) { try { return path.toFile().isDirectory(); - } catch (UnsupportedOperationException e) { + } catch (final UnsupportedOperationException e) { return Files.isDirectory(path); - } catch (SecurityException e) { + } catch (final SecurityException e) { return false; } } @@ -752,7 +752,7 @@ public BasicFileAttributes get(final Path path) { public static BasicFileAttributes readAttributes(final Path path) { try { return Files.readAttributes(path, BasicFileAttributes.class); - } catch (IOException e) { + } catch (final IOException e) { return new BasicFileAttributes() { @Override public FileTime lastModifiedTime() { diff --git a/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java index 1a61ff981..71f277fb8 100644 --- a/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java +++ b/src/test/java/io/github/classgraph/issues/issue854/Issue854Test.java @@ -13,21 +13,22 @@ class Issue854Test { @Test void getFullyQualifiedClassName() { - ClassLoader mainClassLoader = Issue854Test.class.getClassLoader(); - ScanResult scanResult = new ClassGraph().enableClassInfo().enableAnnotationInfo().ignoreClassVisibility() - .ignoreFieldVisibility().ignoreMethodVisibility().overrideClassLoaders(mainClassLoader) - .acceptPackages("com.google.common.collect").scan(); + final ClassLoader mainClassLoader = Issue854Test.class.getClassLoader(); + final ScanResult scanResult = new ClassGraph().enableClassInfo().enableAnnotationInfo() + .ignoreClassVisibility().ignoreFieldVisibility().ignoreMethodVisibility() + .overrideClassLoaders(mainClassLoader).acceptPackages("com.google.common.collect").scan(); - String anonymousClass = "com.google.common.collect.TreeRangeMap$SubRangeMap$1"; - ClassInfo classInfo = scanResult.getClassInfo(anonymousClass); - ClassRefTypeSignature signature = classInfo.getTypeSignatureOrTypeDescriptor().getSuperclassSignature(); + final String anonymousClass = "com.google.common.collect.TreeRangeMap$SubRangeMap$1"; + final ClassInfo classInfo = scanResult.getClassInfo(anonymousClass); + final ClassRefTypeSignature signature = classInfo.getTypeSignatureOrTypeDescriptor() + .getSuperclassSignature(); // Before the fix to 854, this would give the following, because type parameter token parsing // did not stop at '.': // com.google.common.collect.TreeRangeMap$SubRangeMap.SubRangeMapAsMap // But the fully-qualified class name in the classfile is: // com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap - String subRangeMapAsMapClassName = signature.getFullyQualifiedClassName(); + final String subRangeMapAsMapClassName = signature.getFullyQualifiedClassName(); assertThat(subRangeMapAsMapClassName) .isEqualTo("com.google.common.collect.TreeRangeMap$SubRangeMap$SubRangeMapAsMap"); assertNotNull(scanResult.getClassInfo(subRangeMapAsMapClassName)); From 7f546f0da6516d668df28d2826c7d32ce83a88c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 18 Jun 2024 14:03:25 -0600 Subject: [PATCH 661/713] [maven-release-plugin] prepare release classgraph-4.8.174 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 29c3e536b..772bad33b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.174-SNAPSHOT + 4.8.174 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.173 + classgraph-4.8.174 From 8bfaa5776f22ca45ff8cc1c552d55510e754b2dd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 18 Jun 2024 14:03:28 -0600 Subject: [PATCH 662/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 772bad33b..d046c3b98 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.174 + 4.8.175-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 984a2622e694a82ae2ce6381872077e8d2193b0e Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Sat, 17 Aug 2024 13:09:16 +0200 Subject: [PATCH 663/713] Store most used annotation getters --- .../java/io/github/classgraph/ClassInfo.java | 19 ++++++++++++++++--- .../io/github/classgraph/ClassMemberInfo.java | 9 ++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 2f61b7159..a9dd67420 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -179,6 +179,12 @@ public class ClassInfo extends ScanResultObject implements Comparable */ private transient List methodOverrideOrder; + /** The annotations, once they are loaded */ + private ClassInfoList annotationsRef; + + /** The annotation infos, once they are loaded */ + private AnnotationInfoList annotationInfoRef; + // ------------------------------------------------------------------------------------------------------------- /** The modifier bit for annotations. */ @@ -1949,6 +1955,8 @@ public ClassInfoList getClassesImplementing() { * @return the list of annotations and meta-annotations on this class. */ public ClassInfoList getAnnotations() { + if (annotationsRef != null) return annotationsRef; + if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } @@ -1975,13 +1983,14 @@ public ClassInfoList getAnnotations() { if (inheritedSuperclassAnnotations == null) { // No inherited superclass annotations - return new ClassInfoList(annotationClasses, /* sortByName = */ true); + annotationsRef = new ClassInfoList(annotationClasses, /* sortByName = */ true); } else { // Merge inherited superclass annotations and annotations on this class inheritedSuperclassAnnotations.addAll(annotationClasses.reachableClasses); - return new ClassInfoList(inheritedSuperclassAnnotations, annotationClasses.directlyRelatedClasses, + annotationsRef = new ClassInfoList(inheritedSuperclassAnnotations, annotationClasses.directlyRelatedClasses, /* sortByName = */ true); } + return annotationsRef; } /** @@ -2064,10 +2073,14 @@ private ClassInfoList getClassesWithFieldOrMethodAnnotation(final RelType relTyp * none. */ public AnnotationInfoList getAnnotationInfo() { + if (annotationInfoRef != null) return annotationInfoRef; + if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - return AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); + + annotationInfoRef = AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); + return annotationInfoRef; } /** diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java index 1b2acee8a..ad5891d7e 100644 --- a/src/main/java/io/github/classgraph/ClassMemberInfo.java +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -63,6 +63,9 @@ public abstract class ClassMemberInfo extends ScanResultObject implements HasNam /** The annotation on the class member, if any. */ protected AnnotationInfoList annotationInfo; + /** The annotation infos, once they are loaded */ + private AnnotationInfoList annotationInfoRef; + /** Default constructor for deserialization. */ ClassMemberInfo() { super(); @@ -283,11 +286,15 @@ public String getTypeSignatureOrTypeDescriptorStr() { * {@link AnnotationInfo} objects, or the empty list if none. */ public AnnotationInfoList getAnnotationInfo() { + if (annotationInfoRef != null) return annotationInfoRef; + if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST + + annotationInfoRef = annotationInfo == null ? AnnotationInfoList.EMPTY_LIST : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); + return annotationInfoRef; } /** From 1017bacdea7fe6161c7af812914daeed39f9fd3c Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Sat, 17 Aug 2024 13:39:56 +0200 Subject: [PATCH 664/713] Catch NoClassDefFoundError when loading methods, improve docs Those happen if the method returns an unknown type --- .../java/io/github/classgraph/MethodInfo.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 9f264ec78..60c8f28fe 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -656,7 +656,12 @@ private Class[] loadParameterClasses() { * * @return The {@link Method} reference for this method. * @throws IllegalArgumentException - * if the method does not exist, or if the method is a constructor. + *
    + *
  • If the method does not exist
  • + *
  • If the method is a constructor
  • + *
  • If one of the method's parameters references an unknown class
  • + *
  • If the method's return type references an unknown class
  • + *
*/ public Method loadClassAndGetMethod() throws IllegalArgumentException { if (isConstructor()) { @@ -671,7 +676,11 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { return loadClass().getDeclaredMethod(getName(), parameterClassesArr); } catch (final NoSuchMethodException es2) { throw new IllegalArgumentException("Method not found: " + getClassName() + "." + getName()); + } catch (final NoClassDefFoundError e3) { // If the method returns an unknown class, for example + throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e3); } + } catch (final NoClassDefFoundError e4) { // If the method returns an unknown class, for example + throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e4); } } @@ -683,7 +692,11 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { * * @return The {@link Constructor} reference for this constructor. * @throws IllegalArgumentException - * if the constructor does not exist, or if the method is not a constructor. + *
    + *
  • If the constructor does not exist
  • + *
  • If the method is not a constructor
  • + *
  • If one of the constructor's parameters references an unknown class
  • + *
*/ public Constructor loadClassAndGetConstructor() throws IllegalArgumentException { if (!isConstructor()) { From 1b2765bcc299b13f60ac6ba31b8c1aeb550a10f4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 01:31:25 -0600 Subject: [PATCH 665/713] Catch NoClassDefFoundError in ScanResultObject --- .../java/io/github/classgraph/MethodInfo.java | 4 ---- .../io/github/classgraph/ScanResultObject.java | 18 ++++++++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 60c8f28fe..d225502aa 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -676,11 +676,7 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { return loadClass().getDeclaredMethod(getName(), parameterClassesArr); } catch (final NoSuchMethodException es2) { throw new IllegalArgumentException("Method not found: " + getClassName() + "." + getName()); - } catch (final NoClassDefFoundError e3) { // If the method returns an unknown class, for example - throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e3); } - } catch (final NoClassDefFoundError e4) { // If the method returns an unknown class, for example - throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e4); } } diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index 97e24a528..c0db44b40 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -172,16 +172,14 @@ private String getClassInfoNameOrClassName() { Class loadClass(final Class superclassOrInterfaceType, final boolean ignoreExceptions) { if (classRef == null) { final String className = getClassInfoNameOrClassName(); - if (scanResult != null) { - classRef = scanResult.loadClass(className, superclassOrInterfaceType, ignoreExceptions); - } else { - // Fallback, if scanResult is not set - try { - classRef = Class.forName(className); - } catch (final Throwable t) { - if (!ignoreExceptions) { - throw new IllegalArgumentException("Could not load class " + className, t); - } + try { + classRef = scanResult != null + ? scanResult.loadClass(className, superclassOrInterfaceType, ignoreExceptions) + // Fallback, if scanResult is not set + : Class.forName(className); + } catch (final Throwable t) { + if (!ignoreExceptions) { + throw new IllegalArgumentException("Could not load class " + className, t); } } } From 24402f9cbb8aeb1c1c658276f0aeb42364ee590e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 01:42:56 -0600 Subject: [PATCH 666/713] Improve previous commit --- src/main/java/io/github/classgraph/FieldInfo.java | 2 +- src/main/java/io/github/classgraph/MethodInfo.java | 2 ++ src/main/java/io/github/classgraph/ScanResultObject.java | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index cf8bbb7e2..7dcd12a8b 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -253,7 +253,7 @@ public Object getConstantInitializerValue() { * * @return The {@link Field} reference for this field. * @throws IllegalArgumentException - * if the field does not exist. + * if the class can't be loaded or the field does not exist. */ public Field loadClassAndGetField() throws IllegalArgumentException { try { diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index d225502aa..1a05b569b 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -657,6 +657,7 @@ private Class[] loadParameterClasses() { * @return The {@link Method} reference for this method. * @throws IllegalArgumentException *
    + *
  • If the method's class can't be loaded
  • *
  • If the method does not exist
  • *
  • If the method is a constructor
  • *
  • If one of the method's parameters references an unknown class
  • @@ -689,6 +690,7 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { * @return The {@link Constructor} reference for this constructor. * @throws IllegalArgumentException *
      + *
    • If the method's class can't be loaded
    • *
    • If the constructor does not exist
    • *
    • If the method is not a constructor
    • *
    • If one of the constructor's parameters references an unknown class
    • diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index c0db44b40..4332e5a15 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -170,6 +170,7 @@ private String getClassInfoNameOrClassName() { * if the class could not be loaded or cast, and ignoreExceptions was false. */ Class loadClass(final Class superclassOrInterfaceType, final boolean ignoreExceptions) { + // If class is not already loaded, try loading class if (classRef == null) { final String className = getClassInfoNameOrClassName(); try { @@ -177,6 +178,9 @@ Class loadClass(final Class superclassOrInterfaceType, final boolean i ? scanResult.loadClass(className, superclassOrInterfaceType, ignoreExceptions) // Fallback, if scanResult is not set : Class.forName(className); + if (classRef == null && !ignoreExceptions) { + throw new IllegalArgumentException("Could not load class " + className); + } } catch (final Throwable t) { if (!ignoreExceptions) { throw new IllegalArgumentException("Could not load class " + className, t); From 86f35bf971209ef328ccd17856ddcae6e97a78bc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 01:45:59 -0600 Subject: [PATCH 667/713] Update ci.yml Fix Java versions --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d426dbec..8136503c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15', '16', '17', '18', '19' ] + java: [ '8', '11', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From ae6552ffb6c6ba7f734c8e11f97307cdf39d0774 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 01:47:04 -0600 Subject: [PATCH 668/713] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8136503c3..bd45c9684 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22' ] + java: [ '8', '11', '13', '15', '16', '17', '18', '19', '20', '21', '22' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From 70d206c5185b90ecb84438704f563c971b37ac6c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 01:56:01 -0600 Subject: [PATCH 669/713] Update ci.yml JDK 20+ doesn't support source level 7 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd45c9684..d87baada3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '13', '15', '16', '17', '18', '19', '20', '21', '22' ] + java: [ '8', '11', '13', '15', '16', '17', '18', '19' ] steps: - uses: actions/checkout@v4 - name: Set up JDK From 087abf0f8137ebc73a78a59e62d5259007a0bb4e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 18 Aug 2024 02:15:37 -0600 Subject: [PATCH 670/713] Fix warning --- .../fastzipfilereader/NestedJarHandler.java | 289 +++++++++++------- 1 file changed, 185 insertions(+), 104 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 5c38547a4..e8cef97a9 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -87,8 +87,10 @@ public class NestedJarHandler { public ReflectionUtils reflectionUtils; /** - * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} for that file, used to ensure - * that the {@link RandomAccessFile} and {@link FileChannel} for any given zipfile is opened only once. + * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} + * for that file, used to ensure + * that the {@link RandomAccessFile} and {@link FileChannel} for any given + * zipfile is opened only once. */ private SingletonMap // canonicalFileToPhysicalZipFileMap = new SingletonMap() { @@ -99,8 +101,10 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) }; /** - * A singleton map from a {@link FastZipEntry} to the {@link ZipFileSlice} wrapping either the zip entry data, - * if the entry is stored, or a ByteBuffer, if the zip entry was inflated to memory, or a physical file on disk + * A singleton map from a {@link FastZipEntry} to the {@link ZipFileSlice} + * wrapping either the zip entry data, + * if the entry is stored, or a ByteBuffer, if the zip entry was inflated to + * memory, or a physical file on disk * if the zip entry was inflated to a temporary file. */ private SingletonMap // @@ -110,7 +114,8 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode throws IOException, InterruptedException { ZipFileSlice childZipEntrySlice; if (!childZipEntry.isDeflated) { - // The child zip entry is a stored nested zipfile -- wrap it in a new ZipFileSlice. + // The child zip entry is a stored nested zipfile -- wrap it in a new + // ZipFileSlice. // Hopefully nested zipfiles are stored, not deflated, as this is the fast path. childZipEntrySlice = new ZipFileSlice(childZipEntry); @@ -123,7 +128,8 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode + childZipEntry.uncompressedSize); } - // Read the InputStream for the child zip entry to a RAM buffer, or spill to disk if it's too large + // Read the InputStream for the child zip entry to a RAM buffer, or spill to + // disk if it's too large final PhysicalZipFile physicalZipFile = new PhysicalZipFile(childZipEntry.getSlice().open(), childZipEntry.uncompressedSize >= 0L && childZipEntry.uncompressedSize <= FileUtils.MAX_BUFFER_SIZE @@ -138,7 +144,10 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode } }; - /** A singleton map from a {@link ZipFileSlice} to the {@link LogicalZipFile} for that slice. */ + /** + * A singleton map from a {@link ZipFileSlice} to the {@link LogicalZipFile} for + * that slice. + */ private SingletonMap // zipFileSliceToLogicalZipFileMap = new SingletonMap() { @Override @@ -151,10 +160,11 @@ public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode }; /** - * A singleton map from nested jarfile path to a tuple of the logical zipfile for the path, and the package root + * A singleton map from nested jarfile path to a tuple of the logical zipfile + * for the path, and the package root * within the logical zipfile. */ - public SingletonMap, IOException> // + public SingletonMap, IOException> // nestedPathToLogicalZipFileAndPackageRootMap = // new SingletonMap, IOException>() { @Override @@ -163,11 +173,13 @@ public Entry newInstance(final String nestedJarPathRaw, final String nestedJarPath = FastPathResolver.resolve(nestedJarPathRaw); final int lastPlingIdx = nestedJarPath.lastIndexOf('!'); if (lastPlingIdx < 0) { - // nestedJarPath is a simple file path or URL (i.e. doesn't have any '!' sections). + // nestedJarPath is a simple file path or URL (i.e. doesn't have any '!' + // sections). // This is also the last frame of recursion for the 'else' clause below. // If the path starts with "http://" or "https://" or any other URI/URL scheme, - // download the jar to a temp file or to a ByteBuffer in RAM. ("jar:" and "file:" + // download the jar to a temp file or to a ByteBuffer in RAM. ("jar:" and + // "file:" // have already been stripped from any URL/URI.) final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(nestedJarPath).matches(); PhysicalZipFile physicalZipFile; @@ -175,7 +187,8 @@ public Entry newInstance(final String nestedJarPathRaw, final String scheme = nestedJarPath.substring(0, nestedJarPath.indexOf(':')); if (scanSpec.allowedURLSchemes == null || !scanSpec.allowedURLSchemes.contains(scheme)) { - // No URL schemes other than "file:" (with optional "jar:" prefix) allowed (these + // No URL schemes other than "file:" (with optional "jar:" prefix) allowed + // (these // schemes were already stripped by FastPathResolver.resolve(nestedJarPathRaw)) throw new IOException("Scanning of URL scheme \"" + scheme + "\" has not been enabled -- cannot scan classpath element: " @@ -225,9 +238,12 @@ public Entry newInstance(final String nestedJarPathRaw, childPath = FileUtils.sanitizeEntryPath(childPath, /* removeInitialSlash = */ true, /* removeFinalSlash = */ true); - // Recursively remove one '!' section at a time, back towards the beginning of the URL or - // file path. At the last frame of recursion, the toplevel jarfile will be reached and - // returned. The recursion is guaranteed to terminate because parentPath gets one + // Recursively remove one '!' section at a time, back towards the beginning of + // the URL or + // file path. At the last frame of recursion, the toplevel jarfile will be + // reached and + // returned. The recursion is guaranteed to terminate because parentPath gets + // one // '!'-section shorter with each recursion frame. Entry parentLogicalZipFileAndPackageRoot; try { @@ -239,25 +255,29 @@ public Entry newInstance(final String nestedJarPathRaw, throw new IOException("Could not get parent logical zipfile " + parentPath, e); } - // Only the last item in a '!'-delimited list can be a non-jar path, so the parent must + // Only the last item in a '!'-delimited list can be a non-jar path, so the + // parent must // always be a jarfile. final LogicalZipFile parentLogicalZipFile = parentLogicalZipFileAndPackageRoot.getKey(); // Look up the child path within the parent zipfile boolean isDirectory = false; while (childPath.endsWith("/")) { - // Child path is definitely a directory, it ends with a slash + // Child path is definitely a directory, it ends with a slash isDirectory = true; childPath = childPath.substring(0, childPath.length() - 1); } FastZipEntry childZipEntry = null; if (!isDirectory) { // If child path doesn't end with a slash, see if there's a non-directory entry - // with a name matching the child path (LogicalZipFile discards directory entries + // with a name matching the child path (LogicalZipFile discards directory + // entries // ending with a slash when reading the central directory of a zipfile). // N.B. We perform an O(N) search here because we assume the number of classpath - // elements containing "!" sections is relatively small compared to the total number - // of entries in all jarfiles (i.e. building a HashMap of entry path to entry for + // elements containing "!" sections is relatively small compared to the total + // number + // of entries in all jarfiles (i.e. building a HashMap of entry path to entry + // for // every jarfile would generally be more expensive than performing this linear // search, and unless the classpath is enormous, the overall time performance // will not tend towards O(N^2). @@ -269,7 +289,8 @@ public Entry newInstance(final String nestedJarPathRaw, } } if (childZipEntry == null) { - // If there is no non-directory zipfile entry with a name matching the child path, + // If there is no non-directory zipfile entry with a name matching the child + // path, // test to see if any entries in the zipfile have the child path as a dir prefix final String childPathPrefix = childPath + "/"; for (final FastZipEntry entry : parentLogicalZipFile.entries) { @@ -308,10 +329,14 @@ public Entry newInstance(final String nestedJarPathRaw, "Nested jar scanning is disabled -- skipping nested jar " + nestedJarPath); } - // The child path corresponds to a non-directory zip entry, so it must be a nested jar - // (since non-jar nested files cannot be used on the classpath). Map the nested jar as - // a new ZipFileSlice if it is stored, or inflate it to RAM or to a temporary file if - // it is deflated, then create a new ZipFileSlice over the temporary file or ByteBuffer. + // The child path corresponds to a non-directory zip entry, so it must be a + // nested jar + // (since non-jar nested files cannot be used on the classpath). Map the nested + // jar as + // a new ZipFileSlice if it is stored, or inflate it to RAM or to a temporary + // file if + // it is deflated, then create a new ZipFileSlice over the temporary file or + // ByteBuffer. // Get zip entry as a ZipFileSlice, possibly inflating to disk or RAM @@ -347,7 +372,10 @@ public Entry newInstance(final String nestedJarPathRaw, } }; - /** A singleton map from a {@link ModuleRef} to a {@link ModuleReaderProxy} recycler for the module. */ + /** + * A singleton map from a {@link ModuleRef} to a {@link ModuleReaderProxy} + * recycler for the module. + */ public SingletonMap, IOException> // moduleRefToModuleReaderProxyRecyclerMap = // new SingletonMap, IOException>() { @@ -402,9 +430,9 @@ public RecyclableInflater newInstance() throws RuntimeException { * A handler for nested jars. * * @param scanSpec - * The {@link ScanSpec}. + * The {@link ScanSpec}. * @param interruptionChecker - * the interruption checker + * the interruption checker */ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker interruptionChecker, final ReflectionUtils reflectionUtils) { @@ -419,7 +447,7 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter * Get the leafname of a path. * * @param path - * the path + * the path * @return the string */ private static String leafname(final String path) { @@ -430,7 +458,7 @@ private static String leafname(final String path) { * Sanitize filename. * * @param filename - * the filename + * the filename * @return the sanitized filename */ private String sanitizeFilename(final String filename) { @@ -442,12 +470,13 @@ private String sanitizeFilename(final String filename) { * Create a temporary file, and mark it for deletion on exit. * * @param filePathBase - * The path to derive the temporary filename from. + * The path to derive the temporary filename from. * @param onlyUseLeafname - * If true, only use the leafname of filePath to derive the temporary filename. + * If true, only use the leafname of filePath to derive + * the temporary filename. * @return The temporary {@link File}. * @throws IOException - * If the temporary file could not be created. + * If the temporary file could not be created. */ public File makeTempFile(final String filePathBase, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR @@ -461,11 +490,11 @@ public File makeTempFile(final String filePathBase, final boolean onlyUseLeafnam * Attempt to remove a temporary file. * * @param tempFile - * the temp file + * the temp file * @throws IOException - * If the temporary file could not be removed. + * If the temporary file could not be removed. * @throws SecurityException - * If the temporary file is inaccessible. + * If the temporary file is inaccessible. */ void removeTempFile(final File tempFile) throws IOException, SecurityException { if (tempFiles.remove(tempFile)) { @@ -476,12 +505,13 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { } /** - * Mark a {@link Slice} as open, so it can be closed when the {@link ScanResult} is closed. + * Mark a {@link Slice} as open, so it can be closed when the {@link ScanResult} + * is closed. * * @param slice - * the {@link Slice} that was just opened. + * the {@link Slice} that was just opened. * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ public void markSliceAsOpen(final Slice slice) throws IOException { openSlices.add(slice); @@ -491,30 +521,39 @@ public void markSliceAsOpen(final Slice slice) throws IOException { * Mark a {@link Slice} as closed. * * @param slice - * the {@link Slice} to close. + * the {@link Slice} to close. */ public void markSliceAsClosed(final Slice slice) { openSlices.remove(slice); } /** - * Download a jar from a URL to a temporary file, or to a ByteBuffer if the temporary directory is not writeable - * or full. The downloaded jar is returned wrapped in a {@link PhysicalZipFile} instance. + * Download a jar from a URL to a temporary file, or to a ByteBuffer if the + * temporary directory is not writeable + * or full. The downloaded jar is returned wrapped in a {@link PhysicalZipFile} + * instance. * * @param jarURL - * the jar URL + * the jar URL * @param log - * the log - * @return the temporary file or {@link ByteBuffer} the jar was downloaded to, wrapped in a + * the log + * @return the temporary file or {@link ByteBuffer} the jar was downloaded to, + * wrapped in a * {@link PhysicalZipFile} instance. * @throws IOException - * If the jar could not be downloaded, or the jar URL is malformed. + * If the jar could not be downloaded, or the + * jar URL is malformed. * @throws InterruptedException - * if the thread was interrupted + * if the thread was interrupted * @throws IllegalArgumentException - * If the temp dir is not writeable, or has insufficient space to download the jar. (This is thrown - * as a separate exception from IOException, so that the case of an unwriteable temp dir can be - * handled separately, by downloading the jar to a ByteBuffer in RAM.) + * If the temp dir is not writeable, or has + * insufficient space to download the jar. + * (This is thrown + * as a separate exception from IOException, so + * that the case of an unwriteable temp dir can + * be + * handled separately, by downloading the jar + * to a ByteBuffer in RAM.) */ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) throws IOException, InterruptedException { @@ -531,8 +570,9 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo final String scheme = url.getProtocol(); if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) { - // Check if this URL is backed by a filesystem -- if it is, don't download a copy of the file - // over the URL; instead, access the filesystem directly + // Check if this URL is backed by a filesystem -- if it is, don't download a + // copy of the file + // over the URL; instead, access the filesystem directly try { final Path path = Paths.get(url.toURI()); // Fails with FileSystemNotFoundException if filesystem not registered for URL @@ -559,11 +599,14 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo "Got response code " + urlConn.httpConn.getResponseCode() + " for URL " + url); } } else if (url.getProtocol().equalsIgnoreCase("file")) { - // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that + // We ended up with a "file:" URL, which can happen as a result of a custom URL + // scheme that // rewrites its URLs into "file:" URLs (see Issue400.java). try { - // If this is a "file:" URL, get the file from the URL and return it as a new PhysicalZipFile - // (this avoids going through an InputStream). Throws IOException if the file cannot be read. + // If this is a "file:" URL, get the file from the URL and return it as a new + // PhysicalZipFile + // (this avoids going through an InputStream). Throws IOException if the file + // cannot be read. final File file = Paths.get(url.toURI()).toFile(); return new PhysicalZipFile(file, this, log); @@ -617,12 +660,14 @@ public void close() { // ------------------------------------------------------------------------------------------------------------- /** - * Wrapper class that allows an {@link Inflater} instance to be reset for reuse and then recycled by a + * Wrapper class that allows an {@link Inflater} instance to be reset for reuse + * and then recycled by a * {@link Recycler}. */ private static class RecyclableInflater implements Resettable, AutoCloseable { /** - * Create a new {@link Inflater} instance with the "nowrap" option (which is needed for zipfile entries). + * Create a new {@link Inflater} instance with the "nowrap" option (which is + * needed for zipfile entries). */ private final Inflater inflater = new Inflater(/* nowrap = */ true); @@ -636,14 +681,18 @@ public Inflater getInflater() { } /** - * Called when an {@link Inflater} instance is recycled, to reset the inflater so it can accept new input. + * Called when an {@link Inflater} instance is recycled, to reset the inflater + * so it can accept new input. */ @Override public void reset() { inflater.reset(); } - /** Called when the {@link Recycler} instance is closed, to destroy the {@link Inflater} instance. */ + /** + * Called when the {@link Recycler} instance is closed, to destroy the + * {@link Inflater} instance. + */ @Override public void close() { inflater.end(); @@ -651,17 +700,19 @@ public void close() { } /** - * Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling the {@link Inflater} instance. + * Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling + * the {@link Inflater} instance. * * @param rawInputStream - * the raw input stream + * the raw input stream * @return the inflater input stream * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ public InputStream openInflaterInputStream(final InputStream rawInputStream) throws IOException { return new InputStream() { // Gen Inflater instance with nowrap set to true (needed by zip entries) + @SuppressWarnings("resource") private final RecyclableInflater recyclableInflater = inflaterRecycler.acquire(); private final Inflater inflater = recyclableInflater.getInflater(); private final AtomicBoolean closed = new AtomicBoolean(); @@ -693,7 +744,8 @@ public int read(final byte[] outBuf, final int off, final int len) throws IOExce return 0; } try { - // Keep fetching data from rawInputStream until buffer is full or inflater has finished + // Keep fetching data from rawInputStream until buffer is full or inflater has + // finished int totInflatedBytes = 0; while (!inflater.finished() && totInflatedBytes < len) { final int numInflatedBytes = inflater.inflate(outBuf, off + totInflatedBytes, @@ -762,7 +814,8 @@ public int available() throws IOException { throw new IOException("Already closed"); } // We don't know how many bytes are available, but have to return greater than - // zero if there is still input, according to the API contract. Hopefully nothing + // zero if there is still input, according to the API contract. Hopefully + // nothing // relies on this and ends up reading just one byte at a time. return inflater.finished() ? 0 : 1; } @@ -800,34 +853,42 @@ public void close() { // ------------------------------------------------------------------------------------------------------------- /** - * Read all the bytes in an {@link InputStream}, with spillover to a temporary file on disk if a maximum buffer + * Read all the bytes in an {@link InputStream}, with spillover to a temporary + * file on disk if a maximum buffer * size is exceeded. * * @param inputStream - * the {@link InputStream} to read from. + * the {@link InputStream} to read from. * @param tempFileBaseName - * the source URL or zip entry that inputStream was opened from (used to name temporary file, if - * needed). + * the source URL or zip entry that inputStream was + * opened from (used to name temporary file, if + * needed). * @param inputStreamLengthHint - * the length of inputStream if known, else -1L. + * the length of inputStream if known, else -1L. * @param log - * the log. - * @return if the {@link InputStream} could be read into a byte array, an {@link ArraySlice} will be returned. - * If this fails and the {@link InputStream} is spilled over to disk, a {@link FileSlice} will be + * the log. + * @return if the {@link InputStream} could be read into a byte array, an + * {@link ArraySlice} will be returned. + * If this fails and the {@link InputStream} is spilled over to disk, a + * {@link FileSlice} will be * returned. * * @throws IOException - * If the contents could not be read. + * If the contents could not be read. */ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, final String tempFileBaseName, final long inputStreamLengthHint, final LogNode log) throws IOException { // Open an InflaterInputStream on the slice try (InputStream inptStream = inputStream) { if (inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { - // inputStreamLengthHint is unknown (-1) or shorter than scanSpec.maxBufferedJarRAMSize, - // so try reading from the InputStream into an array of size scanSpec.maxBufferedJarRAMSize - // or inputStreamLengthHint respectively. Also if inputStreamLengthHint == 0, which may or - // may not be valid, use a buffer size of 16kB to avoid spilling to disk in case this is + // inputStreamLengthHint is unknown (-1) or shorter than + // scanSpec.maxBufferedJarRAMSize, + // so try reading from the InputStream into an array of size + // scanSpec.maxBufferedJarRAMSize + // or inputStreamLengthHint respectively. Also if inputStreamLengthHint == 0, + // which may or + // may not be valid, use a buffer size of 16kB to avoid spilling to disk in case + // this is // wrong but the file is still small. final int bufSize = inputStreamLengthHint == -1L ? scanSpec.maxBufferedJarRAMSize : inputStreamLengthHint == 0L ? 16384 @@ -842,20 +903,25 @@ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, fina bufBytesUsed += bytesRead; } if (bytesRead == 0) { - // If bytesRead was zero rather than -1, we need to probe the InputStream (by reading - // one more byte) to see if inputStreamHint underestimated the actual length of the stream + // If bytesRead was zero rather than -1, we need to probe the InputStream (by + // reading + // one more byte) to see if inputStreamHint underestimated the actual length of + // the stream final byte[] overflowBuf = new byte[1]; final int overflowBufBytesUsed = inptStream.read(overflowBuf, 0, 1); if (overflowBufBytesUsed == 1) { - // We were able to read one more byte, so we're still not at the end of the stream, + // We were able to read one more byte, so we're still not at the end of the + // stream, // and we need to spill to disk, because buf is full return spillToDisk(inptStream, tempFileBaseName, buf, overflowBuf, log); } - // else (overflowBufBytesUsed == -1), so reached the end of the stream => don't spill to disk + // else (overflowBufBytesUsed == -1), so reached the end of the stream => don't + // spill to disk } // Successfully reached end of stream if (bufBytesUsed < buf.length) { - // Trim array if needed (this is needed if inputStreamLengthHint was -1, or overestimated + // Trim array if needed (this is needed if inputStreamLengthHint was -1, or + // overestimated // the length of the InputStream) buf = Arrays.copyOf(buf, bufBytesUsed); } @@ -864,28 +930,33 @@ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, fina 0L, this); } - // inputStreamLengthHint is longer than scanSpec.maxJarRamSize, so immediately spill to disk + // inputStreamLengthHint is longer than scanSpec.maxJarRamSize, so immediately + // spill to disk return spillToDisk(inptStream, tempFileBaseName, /* buf = */ null, /* overflowBuf = */ null, log); } } /** - * Spill an {@link InputStream} to disk if the stream is too large to fit in RAM. + * Spill an {@link InputStream} to disk if the stream is too large to fit in + * RAM. * * @param inputStream - * The {@link InputStream}. + * The {@link InputStream}. * @param tempFileBaseName - * The stem to base the temporary filename on. + * The stem to base the temporary filename on. * @param buf - * The first buffer to write to the beginning of the file, or null if none. + * The first buffer to write to the beginning of the + * file, or null if none. * @param overflowBuf - * The second buffer to write to the beginning of the file, or null if none. (Should have same - * nullity as buf.) + * The second buffer to write to the beginning of the + * file, or null if none. (Should have same + * nullity as buf.) * @param log - * The log. + * The log. * @return the file slice * @throws IOException - * If anything went wrong creating or writing to the temp file. + * If anything went wrong creating or writing to the temp + * file. */ private FileSlice spillToDisk(final InputStream inputStream, final String tempFileBaseName, final byte[] buf, final byte[] overflowBuf, final LogNode log) throws IOException { @@ -901,7 +972,8 @@ private FileSlice spillToDisk(final InputStream inputStream, final String tempFi + tempFileBaseName + " -> " + tempFile); } - // Copy everything read so far and the rest of the InputStream to the temporary file + // Copy everything read so far and the rest of the InputStream to the temporary + // file try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(tempFile))) { // Write already-read buffered bytes to temp file, if anything was read if (buf != null) { @@ -923,12 +995,13 @@ private FileSlice spillToDisk(final InputStream inputStream, final String tempFi * Read all the bytes in an {@link InputStream}. * * @param inputStream - * The {@link InputStream}. + * The {@link InputStream}. * @param uncompressedLengthHint - * The length of the data once inflated from the {@link InputStream}, if known, otherwise -1L. + * The length of the data once inflated from the + * {@link InputStream}, if known, otherwise -1L. * @return The contents of the {@link InputStream} as a byte array. * @throws IOException - * If the contents could not be read. + * If the contents could not be read. */ public static byte[] readAllBytesAsArray(final InputStream inputStream, final long uncompressedLengthHint) throws IOException { @@ -937,9 +1010,10 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo } try (InputStream inptStream = inputStream) { final int bufferSize = uncompressedLengthHint < 1L - // If fileSizeHint is zero or unknown, use default buffer size + // If fileSizeHint is zero or unknown, use default buffer size ? DEFAULT_BUFFER_SIZE - // fileSizeHint is just a hint -- limit the max allocated buffer size, so that invalid ZipEntry + // fileSizeHint is just a hint -- limit the max allocated buffer size, so that + // invalid ZipEntry // lengths do not become a memory allocation attack vector : Math.min((int) uncompressedLengthHint, MAX_INITIAL_BUFFER_SIZE); byte[] buf = new byte[bufferSize]; @@ -954,8 +1028,10 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo break; } - // bytesRead == 0: either the buffer was the correct size and the end of the stream has been - // reached, or the buffer was too small. Need to try reading one more byte to see which is + // bytesRead == 0: either the buffer was the correct size and the end of the + // stream has been + // reached, or the buffer was too small. Need to try reading one more byte to + // see which is // the case. final int extraByte = inptStream.read(); if (extraByte == -1) { @@ -963,7 +1039,8 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo break; } - // Haven't reached end of stream yet. Need to grow the buffer (double its size), and append + // Haven't reached end of stream yet. Need to grow the buffer (double its size), + // and append // the extra byte that was just read. if (buf.length == FileUtils.MAX_BUFFER_SIZE) { throw new IOException("InputStream too large to read into array"); @@ -979,7 +1056,8 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo // ------------------------------------------------------------------------------------------------------------- /** - * Close zipfiles, modules, and recyclers, and delete temporary files. Called by {@link ScanResult#close()}. + * Close zipfiles, modules, and recyclers, and delete temporary files. Called by + * {@link ScanResult#close()}. * * @param log * The log. @@ -1038,7 +1116,8 @@ public void close(final LogNode log) { inflaterRecycler.forceClose(); inflaterRecycler = null; } - // Temp files have to be deleted last, after all PhysicalZipFiles are closed and files are unmapped + // Temp files have to be deleted last, after all PhysicalZipFiles are closed and + // files are unmapped if (tempFiles != null) { final LogNode rmLog = tempFiles.isEmpty() || log == null ? null : log.log("Removing temporary files"); @@ -1061,7 +1140,9 @@ public void close(final LogNode log) { } } - /** System.runFinalization() -- deprecated in JDK 18, so accessed by reflection. */ + /** + * System.runFinalization() -- deprecated in JDK 18, so accessed by reflection. + */ private static Method runFinalizationMethod; public void runFinalizationMethod() { @@ -1070,7 +1151,7 @@ public void runFinalizationMethod() { } if (runFinalizationMethod != null) { try { - // Call System.runFinalization() (deprecated in JDK 18) + // Call System.runFinalization() (deprecated in JDK 18) runFinalizationMethod.invoke(null); } catch (final Throwable t) { // Ignore From 8aeefba57009199b89ad4c0c3c9132fcf854f8bd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 19 Aug 2024 13:53:34 -0600 Subject: [PATCH 671/713] Add back catch from #879 --- .../java/io/github/classgraph/MethodInfo.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 1a05b569b..9b1755cdb 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -656,13 +656,13 @@ private Class[] loadParameterClasses() { * * @return The {@link Method} reference for this method. * @throws IllegalArgumentException - *
        + *
          *
        • If the method's class can't be loaded
        • *
        • If the method does not exist
        • *
        • If the method is a constructor
        • *
        • If one of the method's parameters references an unknown class
        • *
        • If the method's return type references an unknown class
        • - *
        + *
      */ public Method loadClassAndGetMethod() throws IllegalArgumentException { if (isConstructor()) { @@ -675,9 +675,12 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { } catch (final NoSuchMethodException e1) { try { return loadClass().getDeclaredMethod(getName(), parameterClassesArr); - } catch (final NoSuchMethodException es2) { + } catch (final NoSuchMethodException e2) { throw new IllegalArgumentException("Method not found: " + getClassName() + "." + getName()); } + } catch (final NoClassDefFoundError e3) { + // The method returns an unknown class + throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e3); } } @@ -689,12 +692,12 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { * * @return The {@link Constructor} reference for this constructor. * @throws IllegalArgumentException - *
        + *
          *
        • If the method's class can't be loaded
        • *
        • If the constructor does not exist
        • *
        • If the method is not a constructor
        • *
        • If one of the constructor's parameters references an unknown class
        • - *
        + *
      */ public Constructor loadClassAndGetConstructor() throws IllegalArgumentException { if (!isConstructor()) { @@ -708,9 +711,12 @@ public Constructor loadClassAndGetConstructor() throws IllegalArgumentExcepti } catch (final NoSuchMethodException e1) { try { return loadClass().getDeclaredConstructor(parameterClassesArr); - } catch (final NoSuchMethodException es2) { + } catch (final NoSuchMethodException e2) { throw new IllegalArgumentException("Constructor not found for class " + getClassName()); } + } catch (final NoClassDefFoundError e3) { + // The method returns an unknown class + throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e3); } } From 47605ac6705b7879263306e68f31f4b199e475a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 20 Aug 2024 00:50:35 -0600 Subject: [PATCH 672/713] Remove unnecessary catch block, #879 --- src/main/java/io/github/classgraph/MethodInfo.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 9b1755cdb..0c18778f8 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -714,9 +714,6 @@ public Constructor loadClassAndGetConstructor() throws IllegalArgumentExcepti } catch (final NoSuchMethodException e2) { throw new IllegalArgumentException("Constructor not found for class " + getClassName()); } - } catch (final NoClassDefFoundError e3) { - // The method returns an unknown class - throw new IllegalArgumentException("Could not load method: " + getClassName() + "." + getName(), e3); } } From 688313894de1f65bb46af551babaee8cdf01294a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 20 Aug 2024 01:00:29 -0600 Subject: [PATCH 673/713] [maven-release-plugin] prepare release classgraph-4.8.175 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d046c3b98..3c34e9882 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.175-SNAPSHOT + 4.8.175 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.174 + classgraph-4.8.175 From b1ba93341b2a33458d144eb1b4c39e9593b4a704 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 20 Aug 2024 01:00:31 -0600 Subject: [PATCH 674/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c34e9882..7f667dbd5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.175 + 4.8.176-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 5a5c84cd9ac23964b55f42562075bd6fa1b66d9d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Sep 2024 13:55:29 -0600 Subject: [PATCH 675/713] Add synchronized block to methods with side effects (#883) --- .../java/io/github/classgraph/ClassInfo.java | 136 ++++--- .../io/github/classgraph/ClassMemberInfo.java | 17 +- .../java/io/github/classgraph/FieldInfo.java | 68 ++-- .../java/io/github/classgraph/MethodInfo.java | 364 +++++++++--------- .../java/io/github/classgraph/ScanResult.java | 40 +- .../github/classgraph/ScanResultObject.java | 36 +- 6 files changed, 349 insertions(+), 312 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index a9dd67420..c85ee2f20 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1955,42 +1955,46 @@ public ClassInfoList getClassesImplementing() { * @return the list of annotations and meta-annotations on this class. */ public ClassInfoList getAnnotations() { - if (annotationsRef != null) return annotationsRef; + synchronized (this) { + if (annotationsRef != null) { + return annotationsRef; + } - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } + if (!scanResult.scanSpec.enableAnnotationInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); + } - // Get all annotations on this class - final ReachableAndDirectlyRelatedClasses annotationClasses = this.filterClassInfo(RelType.CLASS_ANNOTATIONS, - /* strictAccept = */ false); - // Check for any @Inherited annotations on superclasses - Set inheritedSuperclassAnnotations = null; - for (final ClassInfo superclass : getSuperclasses()) { - for (final ClassInfo superclassAnnotation : superclass.filterClassInfo(RelType.CLASS_ANNOTATIONS, - /* strictAccept = */ false).reachableClasses) { - // Check if any of the meta-annotations on this annotation are @Inherited, - // which causes an annotation to annotate a class and all of its subclasses. - if (superclassAnnotation != null && superclassAnnotation.isInherited) { - // superclassAnnotation has an @Inherited meta-annotation - if (inheritedSuperclassAnnotations == null) { - inheritedSuperclassAnnotations = new LinkedHashSet<>(); + // Get all annotations on this class + final ReachableAndDirectlyRelatedClasses annotationClasses = this + .filterClassInfo(RelType.CLASS_ANNOTATIONS, /* strictAccept = */ false); + // Check for any @Inherited annotations on superclasses + Set inheritedSuperclassAnnotations = null; + for (final ClassInfo superclass : getSuperclasses()) { + for (final ClassInfo superclassAnnotation : superclass.filterClassInfo(RelType.CLASS_ANNOTATIONS, + /* strictAccept = */ false).reachableClasses) { + // Check if any of the meta-annotations on this annotation are @Inherited, + // which causes an annotation to annotate a class and all of its subclasses. + if (superclassAnnotation != null && superclassAnnotation.isInherited) { + // superclassAnnotation has an @Inherited meta-annotation + if (inheritedSuperclassAnnotations == null) { + inheritedSuperclassAnnotations = new LinkedHashSet<>(); + } + inheritedSuperclassAnnotations.add(superclassAnnotation); } - inheritedSuperclassAnnotations.add(superclassAnnotation); } } - } - if (inheritedSuperclassAnnotations == null) { - // No inherited superclass annotations - annotationsRef = new ClassInfoList(annotationClasses, /* sortByName = */ true); - } else { - // Merge inherited superclass annotations and annotations on this class - inheritedSuperclassAnnotations.addAll(annotationClasses.reachableClasses); - annotationsRef = new ClassInfoList(inheritedSuperclassAnnotations, annotationClasses.directlyRelatedClasses, - /* sortByName = */ true); + if (inheritedSuperclassAnnotations == null) { + // No inherited superclass annotations + annotationsRef = new ClassInfoList(annotationClasses, /* sortByName = */ true); + } else { + // Merge inherited superclass annotations and annotations on this class + inheritedSuperclassAnnotations.addAll(annotationClasses.reachableClasses); + annotationsRef = new ClassInfoList(inheritedSuperclassAnnotations, + annotationClasses.directlyRelatedClasses, /* sortByName = */ true); + } + return annotationsRef; } - return annotationsRef; } /** @@ -2073,14 +2077,18 @@ private ClassInfoList getClassesWithFieldOrMethodAnnotation(final RelType relTyp * none. */ public AnnotationInfoList getAnnotationInfo() { - if (annotationInfoRef != null) return annotationInfoRef; + synchronized (this) { + if (annotationInfoRef != null) { + return annotationInfoRef; + } - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } + if (!scanResult.scanSpec.enableAnnotationInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); + } - annotationInfoRef = AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); - return annotationInfoRef; + annotationInfoRef = AnnotationInfoList.getIndirectAnnotations(annotationInfo, this); + return annotationInfoRef; + } } /** @@ -2186,14 +2194,16 @@ public AnnotationParameterValueList getAnnotationDefaultParameterValues() { if (!isAnnotation()) { throw new IllegalArgumentException("Class is not an annotation: " + getName()); } - if (annotationDefaultParamValues == null) { - return AnnotationParameterValueList.EMPTY_LIST; - } - if (!annotationDefaultParamValuesHasBeenConvertedToPrimitive) { - annotationDefaultParamValues.convertWrapperArraysToPrimitiveArrays(this); - annotationDefaultParamValuesHasBeenConvertedToPrimitive = true; + synchronized (this) { + if (annotationDefaultParamValues == null) { + return AnnotationParameterValueList.EMPTY_LIST; + } + if (!annotationDefaultParamValuesHasBeenConvertedToPrimitive) { + annotationDefaultParamValues.convertWrapperArraysToPrimitiveArrays(this); + annotationDefaultParamValuesHasBeenConvertedToPrimitive = true; + } + return annotationDefaultParamValues; } - return annotationDefaultParamValues; } /** @@ -2955,21 +2965,23 @@ ClassInfoList getClassesWithFieldAnnotationDirectOnly() { * classfile). */ public ClassTypeSignature getTypeSignature() { - if (typeSignatureStr == null) { - return null; - } - if (typeSignature == null) { - try { - typeSignature = ClassTypeSignature.parse(typeSignatureStr, this); - typeSignature.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - for (final ClassTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeSignature); + synchronized (this) { + if (typeSignatureStr == null) { + return null; + } + if (typeSignature == null) { + try { + typeSignature = ClassTypeSignature.parse(typeSignatureStr, this); + typeSignature.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final ClassTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeSignature); + } } + } catch (final ParseException e) { + throw new IllegalArgumentException("Invalid type signature for class " + getName() + + " in classpath element " + getClasspathElementURI() + " : " + typeSignatureStr, e); } - } catch (final ParseException e) { - throw new IllegalArgumentException("Invalid type signature for class " + getName() - + " in classpath element " + getClasspathElementURI() + " : " + typeSignatureStr, e); } } return typeSignature; @@ -3014,12 +3026,14 @@ public ClassTypeSignature getTypeSignatureOrTypeDescriptor() { * @return The synthetic type descriptor for the class. */ public ClassTypeSignature getTypeDescriptor() { - if (typeDescriptor == null) { - typeDescriptor = new ClassTypeSignature(this, getSuperclass(), getInterfaces()); - typeDescriptor.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - for (final ClassTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeDescriptor); + synchronized (this) { + if (typeDescriptor == null) { + typeDescriptor = new ClassTypeSignature(this, getSuperclass(), getInterfaces()); + typeDescriptor.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final ClassTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } } } } diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java index ad5891d7e..84b0bfaad 100644 --- a/src/main/java/io/github/classgraph/ClassMemberInfo.java +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -286,15 +286,18 @@ public String getTypeSignatureOrTypeDescriptorStr() { * {@link AnnotationInfo} objects, or the empty list if none. */ public AnnotationInfoList getAnnotationInfo() { - if (annotationInfoRef != null) return annotationInfoRef; + synchronized (this) { + if (annotationInfoRef != null) + return annotationInfoRef; - if (!scanResult.scanSpec.enableAnnotationInfo) { - throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); - } + if (!scanResult.scanSpec.enableAnnotationInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); + } - annotationInfoRef = annotationInfo == null ? AnnotationInfoList.EMPTY_LIST - : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); - return annotationInfoRef; + annotationInfoRef = annotationInfo == null ? AnnotationInfoList.EMPTY_LIST + : AnnotationInfoList.getIndirectAnnotations(annotationInfo, /* annotatedClass = */ null); + return annotationInfoRef; + } } /** diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 7dcd12a8b..6c0f60860 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -148,23 +148,25 @@ public boolean isEnum() { */ @Override public TypeSignature getTypeDescriptor() { - if (typeDescriptorStr == null) { - return null; - } - if (typeDescriptor == null) { - try { - typeDescriptor = TypeSignature.parse(typeDescriptorStr, declaringClassName); - typeDescriptor.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - for (final TypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeDescriptor); + synchronized (this) { + if (typeDescriptorStr == null) { + return null; + } + if (typeDescriptor == null) { + try { + typeDescriptor = TypeSignature.parse(typeDescriptorStr, declaringClassName); + typeDescriptor.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final TypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } } + } catch (final ParseException e) { + throw new IllegalArgumentException(e); } - } catch (final ParseException e) { - throw new IllegalArgumentException(e); } + return typeDescriptor; } - return typeDescriptor; } /** @@ -180,29 +182,31 @@ public TypeSignature getTypeDescriptor() { */ @Override public TypeSignature getTypeSignature() { - if (typeSignatureStr == null) { - return null; - } - if (typeSignature == null) { - try { - typeSignature = TypeSignature.parse(typeSignatureStr, declaringClassName); - typeSignature.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - for (final TypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeSignature); + synchronized (this) { + if (typeSignatureStr == null) { + return null; + } + if (typeSignature == null) { + try { + typeSignature = TypeSignature.parse(typeSignatureStr, declaringClassName); + typeSignature.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final TypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeSignature); + } } + } catch (final ParseException e) { + throw new IllegalArgumentException( + "Invalid type signature for field " + getClassName() + "." + getName() + + (getClassInfo() != null + ? " in classpath element " + getClassInfo().getClasspathElementURI() + : "") + + " : " + typeSignatureStr, + e); } - } catch (final ParseException e) { - throw new IllegalArgumentException( - "Invalid type signature for field " + getClassName() + "." + getName() - + (getClassInfo() != null - ? " in classpath element " + getClassInfo().getClasspathElementURI() - : "") - + " : " + typeSignatureStr, - e); } + return typeSignature; } - return typeSignature; } /** diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 0c18778f8..b688773c7 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -182,59 +182,62 @@ public String getModifiersStr() { */ @Override public MethodTypeSignature getTypeDescriptor() { - if (typeDescriptor == null) { - try { - typeDescriptor = MethodTypeSignature.parse(typeDescriptorStr, declaringClassName); - typeDescriptor.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - // It is possible that there are extra implicit params added at the beginning of the - // parameter list that type annotations don't take into account. Assume that the - // type signature has the correct number of parameters, and temporarily remove any - // implicit prefix parameters during the type decoration process. See getParameterInfo(). - int sigNumParam = 0; - final MethodTypeSignature sig = getTypeSignature(); - if (sig == null) { - // There is no type signature -- run type annotation decorators on descriptor - for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeDescriptor); - } - } else { - // Determine how many extra implicit params there are - sigNumParam = sig.getParameterTypeSignatures().size(); - final int descNumParam = typeDescriptor.getParameterTypeSignatures().size(); - final int numImplicitPrefixParams = descNumParam - sigNumParam; - if (numImplicitPrefixParams < 0) { - // Sanity check -- should not happen - throw new IllegalArgumentException( - "Fewer params in method type descriptor than in method type signature"); - } else if (numImplicitPrefixParams == 0) { - // There are no implicit prefix params -- run type annotation decorators on descriptor + synchronized (this) { + if (typeDescriptor == null) { + try { + typeDescriptor = MethodTypeSignature.parse(typeDescriptorStr, declaringClassName); + typeDescriptor.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + // It is possible that there are extra implicit params added at the beginning of the + // parameter list that type annotations don't take into account. Assume that the + // type signature has the correct number of parameters, and temporarily remove any + // implicit prefix parameters during the type decoration process. See getParameterInfo(). + int sigNumParam = 0; + final MethodTypeSignature sig = getTypeSignature(); + if (sig == null) { + // There is no type signature -- run type annotation decorators on descriptor for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { decorator.decorate(typeDescriptor); } } else { - // There are implicit prefix params -- strip them temporarily from type descriptor, - // then run decorators, then add them back again - final List paramSigs = typeDescriptor.getParameterTypeSignatures(); - final List strippedParamSigs = paramSigs.subList(0, - numImplicitPrefixParams); - for (int i = 0; i < numImplicitPrefixParams; i++) { - paramSigs.remove(0); - } - for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeDescriptor); - } - for (int i = numImplicitPrefixParams - 1; i >= 0; --i) { - paramSigs.add(0, strippedParamSigs.get(i)); + // Determine how many extra implicit params there are + sigNumParam = sig.getParameterTypeSignatures().size(); + final int descNumParam = typeDescriptor.getParameterTypeSignatures().size(); + final int numImplicitPrefixParams = descNumParam - sigNumParam; + if (numImplicitPrefixParams < 0) { + // Sanity check -- should not happen + throw new IllegalArgumentException( + "Fewer params in method type descriptor than in method type signature"); + } else if (numImplicitPrefixParams == 0) { + // There are no implicit prefix params -- + // run type annotation decorators on descriptor + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + } else { + // There are implicit prefix params -- strip them temporarily from type descriptor, + // then run decorators, then add them back again + final List paramSigs = typeDescriptor.getParameterTypeSignatures(); + final List strippedParamSigs = paramSigs.subList(0, + numImplicitPrefixParams); + for (int i = 0; i < numImplicitPrefixParams; i++) { + paramSigs.remove(0); + } + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeDescriptor); + } + for (int i = numImplicitPrefixParams - 1; i >= 0; --i) { + paramSigs.add(0, strippedParamSigs.get(i)); + } } } } + } catch (final ParseException e) { + throw new IllegalArgumentException(e); } - } catch (final ParseException e) { - throw new IllegalArgumentException(e); } + return typeDescriptor; } - return typeDescriptor; } /** @@ -250,26 +253,28 @@ public MethodTypeSignature getTypeDescriptor() { */ @Override public MethodTypeSignature getTypeSignature() { - if (typeSignature == null && typeSignatureStr != null) { - try { - typeSignature = MethodTypeSignature.parse(typeSignatureStr, declaringClassName); - typeSignature.setScanResult(scanResult); - if (typeAnnotationDecorators != null) { - for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { - decorator.decorate(typeSignature); + synchronized (this) { + if (typeSignature == null && typeSignatureStr != null) { + try { + typeSignature = MethodTypeSignature.parse(typeSignatureStr, declaringClassName); + typeSignature.setScanResult(scanResult); + if (typeAnnotationDecorators != null) { + for (final MethodTypeAnnotationDecorator decorator : typeAnnotationDecorators) { + decorator.decorate(typeSignature); + } } + } catch (final ParseException e) { + throw new IllegalArgumentException( + "Invalid type signature for method " + getClassName() + "." + getName() + + (getClassInfo() != null + ? " in classpath element " + getClassInfo().getClasspathElementURI() + : "") + + " : " + typeSignatureStr, + e); } - } catch (final ParseException e) { - throw new IllegalArgumentException( - "Invalid type signature for method " + getClassName() + "." + getName() - + (getClassInfo() != null - ? " in classpath element " + getClassInfo().getClasspathElementURI() - : "") - + " : " + typeSignatureStr, - e); } + return typeSignature; } - return typeSignature; } /** @@ -300,17 +305,19 @@ public MethodTypeSignature getTypeSignatureOrTypeDescriptor() { * @return The list of exceptions thrown by the method, as a {@link ClassInfoList} (the list may be empty). */ public ClassInfoList getThrownExceptions() { - if (thrownExceptions == null && thrownExceptionNames != null) { - thrownExceptions = new ClassInfoList(thrownExceptionNames.length); - for (final String thrownExceptionName : thrownExceptionNames) { - final ClassInfo classInfo = scanResult.getClassInfo(thrownExceptionName); - if (classInfo != null) { - thrownExceptions.add(classInfo); - classInfo.setScanResult(scanResult); + synchronized (this) { + if (thrownExceptions == null && thrownExceptionNames != null) { + thrownExceptions = new ClassInfoList(thrownExceptionNames.length); + for (final String thrownExceptionName : thrownExceptionNames) { + final ClassInfo classInfo = scanResult.getClassInfo(thrownExceptionName); + if (classInfo != null) { + thrownExceptions.add(classInfo); + classInfo.setScanResult(scanResult); + } } } + return thrownExceptions == null ? ClassInfoList.EMPTY_LIST : thrownExceptions; } - return thrownExceptions == null ? ClassInfoList.EMPTY_LIST : thrownExceptions; } /** @@ -452,134 +459,137 @@ public MethodParameterInfo[] getParameterInfo() { // This was also triggered by an implicit param in Guava 28.2 (#660). - if (parameterInfo == null) { - // Get param type signatures from the type signature of the method - List paramTypeSignatures = null; - final MethodTypeSignature typeSig = getTypeSignature(); - if (typeSig != null) { - paramTypeSignatures = typeSig.getParameterTypeSignatures(); - } + synchronized (this) { + if (parameterInfo == null) { + // Get param type signatures from the type signature of the method + List paramTypeSignatures = null; + final MethodTypeSignature typeSig = getTypeSignature(); + if (typeSig != null) { + paramTypeSignatures = typeSig.getParameterTypeSignatures(); + } - // If there is no type signature (i.e. if this is not a generic method), fall back to the type - // descriptor (N.B. the type descriptor is basically junk, because the compiler may prepend - // `synthetic` and/or `bridge` parameters automatically, without providing any modifiers for - // the method, so that it is impossible to know how many parameters have been prepended -- - // see #660.) - List paramTypeDescriptors = null; - try { - final MethodTypeSignature typeDesc = getTypeDescriptor(); - if (typeDesc != null) { - paramTypeDescriptors = typeDesc.getParameterTypeSignatures(); + // If there is no type signature (i.e. if this is not a generic method), fall back to the type + // descriptor (N.B. the type descriptor is basically junk, because the compiler may prepend + // `synthetic` and/or `bridge` parameters automatically, without providing any modifiers for + // the method, so that it is impossible to know how many parameters have been prepended -- + // see #660.) + List paramTypeDescriptors = null; + try { + final MethodTypeSignature typeDesc = getTypeDescriptor(); + if (typeDesc != null) { + paramTypeDescriptors = typeDesc.getParameterTypeSignatures(); + } + } catch (final Exception e) { + // Ignore any IllegalArgumentExceptions triggered when type annotations are not able to be + /// aligned with parameters, when there is a `synthetic`, `bridge` or `mandated` parameter + // added to the first parameter position. } - } catch (final Exception e) { - // Ignore any IllegalArgumentExceptions triggered when type annotations are not able to be - /// aligned with parameters, when there is a `synthetic`, `bridge` or `mandated` parameter - // added to the first parameter position. - } - // Find the max length of all the parameter information sources - int numParams = paramTypeSignatures == null ? 0 : paramTypeSignatures.size(); - if (paramTypeDescriptors != null && paramTypeDescriptors.size() > numParams) { - numParams = paramTypeDescriptors.size(); - } - if (parameterNames != null && parameterNames.length > numParams) { - numParams = parameterNames.length; - } - if (parameterModifiers != null && parameterModifiers.length > numParams) { - numParams = parameterModifiers.length; - } - if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > numParams) { - numParams = parameterAnnotationInfo.length; - } + // Find the max length of all the parameter information sources + int numParams = paramTypeSignatures == null ? 0 : paramTypeSignatures.size(); + if (paramTypeDescriptors != null && paramTypeDescriptors.size() > numParams) { + numParams = paramTypeDescriptors.size(); + } + if (parameterNames != null && parameterNames.length > numParams) { + numParams = parameterNames.length; + } + if (parameterModifiers != null && parameterModifiers.length > numParams) { + numParams = parameterModifiers.length; + } + if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > numParams) { + numParams = parameterAnnotationInfo.length; + } - // "Right-align" all parameter info, i.e. assume that any automatically-added implicit parameters - // were added at the beginning of the parameter list, not the end. + // "Right-align" all parameter info, i.e. assume that any automatically-added implicit parameters + // were added at the beginning of the parameter list, not the end. - String[] paramNamesAligned = null; - if (parameterNames != null && parameterNames.length > 0) { - if (parameterNames.length == numParams) { - // No alignment necessary - paramNamesAligned = parameterNames; - } else { - // Right-align when not the right length - paramNamesAligned = new String[numParams]; - for (int i = 0, lenDiff = numParams - parameterNames.length; i < parameterNames.length; i++) { - paramNamesAligned[lenDiff + i] = parameterNames[i]; + String[] paramNamesAligned = null; + if (parameterNames != null && parameterNames.length > 0) { + if (parameterNames.length == numParams) { + // No alignment necessary + paramNamesAligned = parameterNames; + } else { + // Right-align when not the right length + paramNamesAligned = new String[numParams]; + for (int i = 0, + lenDiff = numParams - parameterNames.length; i < parameterNames.length; i++) { + paramNamesAligned[lenDiff + i] = parameterNames[i]; + } } } - } - int[] paramModifiersAligned = null; - if (parameterModifiers != null && parameterModifiers.length > 0) { - if (parameterModifiers.length == numParams) { - // No alignment necessary - paramModifiersAligned = parameterModifiers; - } else { - // Right-align when not the right length - paramModifiersAligned = new int[numParams]; - for (int i = 0, - lenDiff = numParams - parameterModifiers.length; i < parameterModifiers.length; i++) { - paramModifiersAligned[lenDiff + i] = parameterModifiers[i]; + int[] paramModifiersAligned = null; + if (parameterModifiers != null && parameterModifiers.length > 0) { + if (parameterModifiers.length == numParams) { + // No alignment necessary + paramModifiersAligned = parameterModifiers; + } else { + // Right-align when not the right length + paramModifiersAligned = new int[numParams]; + for (int i = 0, lenDiff = numParams + - parameterModifiers.length; i < parameterModifiers.length; i++) { + paramModifiersAligned[lenDiff + i] = parameterModifiers[i]; + } } } - } - AnnotationInfo[][] paramAnnotationInfoAligned = null; - if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > 0) { - if (parameterAnnotationInfo.length == numParams) { - // No alignment necessary - paramAnnotationInfoAligned = parameterAnnotationInfo; - } else { - // Right-align when not the right length - paramAnnotationInfoAligned = new AnnotationInfo[numParams][]; - for (int i = 0, lenDiff = numParams - - parameterAnnotationInfo.length; i < parameterAnnotationInfo.length; i++) { - paramAnnotationInfoAligned[lenDiff + i] = parameterAnnotationInfo[i]; + AnnotationInfo[][] paramAnnotationInfoAligned = null; + if (parameterAnnotationInfo != null && parameterAnnotationInfo.length > 0) { + if (parameterAnnotationInfo.length == numParams) { + // No alignment necessary + paramAnnotationInfoAligned = parameterAnnotationInfo; + } else { + // Right-align when not the right length + paramAnnotationInfoAligned = new AnnotationInfo[numParams][]; + for (int i = 0, lenDiff = numParams + - parameterAnnotationInfo.length; i < parameterAnnotationInfo.length; i++) { + paramAnnotationInfoAligned[lenDiff + i] = parameterAnnotationInfo[i]; + } } } - } - List paramTypeSignaturesAligned = null; - if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) { - if (paramTypeSignatures.size() == numParams) { - // No alignment necessary - paramTypeSignaturesAligned = paramTypeSignatures; - } else { - // Right-align when not the right length - paramTypeSignaturesAligned = new ArrayList<>(numParams); - for (int i = 0, lenDiff = numParams - paramTypeSignatures.size(); i < lenDiff; i++) { - // Left-pad with nulls - paramTypeSignaturesAligned.add(null); + List paramTypeSignaturesAligned = null; + if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) { + if (paramTypeSignatures.size() == numParams) { + // No alignment necessary + paramTypeSignaturesAligned = paramTypeSignatures; + } else { + // Right-align when not the right length + paramTypeSignaturesAligned = new ArrayList<>(numParams); + for (int i = 0, lenDiff = numParams - paramTypeSignatures.size(); i < lenDiff; i++) { + // Left-pad with nulls + paramTypeSignaturesAligned.add(null); + } + paramTypeSignaturesAligned.addAll(paramTypeSignatures); } - paramTypeSignaturesAligned.addAll(paramTypeSignatures); } - } - List paramTypeDescriptorsAligned = null; - if (paramTypeDescriptors != null && paramTypeDescriptors.size() > 0) { - if (paramTypeDescriptors.size() == numParams) { - // No alignment necessary - paramTypeDescriptorsAligned = paramTypeDescriptors; - } else { - // Right-align when not the right length - paramTypeDescriptorsAligned = new ArrayList<>(numParams); - for (int i = 0, lenDiff = numParams - paramTypeDescriptors.size(); i < lenDiff; i++) { - // Left-pad with nulls - paramTypeDescriptorsAligned.add(null); + List paramTypeDescriptorsAligned = null; + if (paramTypeDescriptors != null && paramTypeDescriptors.size() > 0) { + if (paramTypeDescriptors.size() == numParams) { + // No alignment necessary + paramTypeDescriptorsAligned = paramTypeDescriptors; + } else { + // Right-align when not the right length + paramTypeDescriptorsAligned = new ArrayList<>(numParams); + for (int i = 0, lenDiff = numParams - paramTypeDescriptors.size(); i < lenDiff; i++) { + // Left-pad with nulls + paramTypeDescriptorsAligned.add(null); + } + paramTypeDescriptorsAligned.addAll(paramTypeDescriptors); } - paramTypeDescriptorsAligned.addAll(paramTypeDescriptors); } - } - // Generate MethodParameterInfo entries - parameterInfo = new MethodParameterInfo[numParams]; - for (int i = 0; i < numParams; i++) { - parameterInfo[i] = new MethodParameterInfo(this, - paramAnnotationInfoAligned == null ? null : paramAnnotationInfoAligned[i], - paramModifiersAligned == null ? 0 : paramModifiersAligned[i], - paramTypeDescriptorsAligned == null ? null : paramTypeDescriptorsAligned.get(i), - paramTypeSignaturesAligned == null ? null : paramTypeSignaturesAligned.get(i), - paramNamesAligned == null ? null : paramNamesAligned[i]); - parameterInfo[i].setScanResult(scanResult); + // Generate MethodParameterInfo entries + parameterInfo = new MethodParameterInfo[numParams]; + for (int i = 0; i < numParams; i++) { + parameterInfo[i] = new MethodParameterInfo(this, + paramAnnotationInfoAligned == null ? null : paramAnnotationInfoAligned[i], + paramModifiersAligned == null ? 0 : paramModifiersAligned[i], + paramTypeDescriptorsAligned == null ? null : paramTypeDescriptorsAligned.get(i), + paramTypeSignaturesAligned == null ? null : paramTypeSignaturesAligned.get(i), + paramNamesAligned == null ? null : paramNamesAligned[i]); + parameterInfo[i].setScanResult(scanResult); + } } + return parameterInfo; } - return parameterInfo; } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b9e10fa18..603593818 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -481,16 +481,18 @@ public ModulePathInfo getModulePathInfo() { * @return A list of all resources (including classfiles and non-classfiles) found in accepted packages. */ public ResourceList getAllResources() { - if (allAcceptedResourcesCached == null) { - // Index Resource objects by path - final ResourceList acceptedResourcesList = new ResourceList(); - for (final ClasspathElement classpathElt : classpathOrder) { - acceptedResourcesList.addAll(classpathElt.acceptedResources); + synchronized (this) { + if (allAcceptedResourcesCached == null) { + // Index Resource objects by path + final ResourceList acceptedResourcesList = new ResourceList(); + for (final ClasspathElement classpathElt : classpathOrder) { + acceptedResourcesList.addAll(classpathElt.acceptedResources); + } + // Set atomically for thread safety + allAcceptedResourcesCached = acceptedResourcesList; } - // Set atomically for thread safety - allAcceptedResourcesCached = acceptedResourcesList; + return allAcceptedResourcesCached; } - return allAcceptedResourcesCached; } /** @@ -501,19 +503,21 @@ public ResourceList getAllResources() { * non-classfiles) found in accepted packages. */ public Map getAllResourcesAsMap() { - if (pathToAcceptedResourcesCached == null) { - final Map pathToAcceptedResourceListMap = new HashMap<>(); - for (final Resource res : getAllResources()) { - ResourceList resList = pathToAcceptedResourceListMap.get(res.getPath()); - if (resList == null) { - pathToAcceptedResourceListMap.put(res.getPath(), resList = new ResourceList()); + synchronized (this) { + if (pathToAcceptedResourcesCached == null) { + final Map pathToAcceptedResourceListMap = new HashMap<>(); + for (final Resource res : getAllResources()) { + ResourceList resList = pathToAcceptedResourceListMap.get(res.getPath()); + if (resList == null) { + pathToAcceptedResourceListMap.put(res.getPath(), resList = new ResourceList()); + } + resList.add(res); } - resList.add(res); + // Set atomically for thread safety + pathToAcceptedResourcesCached = pathToAcceptedResourceListMap; } - // Set atomically for thread safety - pathToAcceptedResourcesCached = pathToAcceptedResourceListMap; + return pathToAcceptedResourcesCached; } - return pathToAcceptedResourcesCached; } /** diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index 4332e5a15..20d577adc 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -170,26 +170,28 @@ private String getClassInfoNameOrClassName() { * if the class could not be loaded or cast, and ignoreExceptions was false. */ Class loadClass(final Class superclassOrInterfaceType, final boolean ignoreExceptions) { - // If class is not already loaded, try loading class - if (classRef == null) { - final String className = getClassInfoNameOrClassName(); - try { - classRef = scanResult != null - ? scanResult.loadClass(className, superclassOrInterfaceType, ignoreExceptions) - // Fallback, if scanResult is not set - : Class.forName(className); - if (classRef == null && !ignoreExceptions) { - throw new IllegalArgumentException("Could not load class " + className); - } - } catch (final Throwable t) { - if (!ignoreExceptions) { - throw new IllegalArgumentException("Could not load class " + className, t); + synchronized (this) { + // If class is not already loaded, try loading class + if (classRef == null) { + final String className = getClassInfoNameOrClassName(); + try { + classRef = scanResult != null + ? scanResult.loadClass(className, superclassOrInterfaceType, ignoreExceptions) + // Fallback, if scanResult is not set + : Class.forName(className); + if (classRef == null && !ignoreExceptions) { + throw new IllegalArgumentException("Could not load class " + className); + } + } catch (final Throwable t) { + if (!ignoreExceptions) { + throw new IllegalArgumentException("Could not load class " + className, t); + } } } + @SuppressWarnings("unchecked") + final Class classT = (Class) classRef; + return classT; } - @SuppressWarnings("unchecked") - final Class classT = (Class) classRef; - return classT; } /** From dc2bdb7ca7cc6534988439a68e8c49abfb0e07a3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Sep 2024 13:55:55 -0600 Subject: [PATCH 676/713] [maven-release-plugin] prepare release classgraph-4.8.176 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7f667dbd5..352f95144 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.176-SNAPSHOT + 4.8.176 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.175 + classgraph-4.8.176 From b61f6e0f774a99fbc49350e75ec85f2fa36f2eed Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Sep 2024 13:55:58 -0600 Subject: [PATCH 677/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 352f95144..039340989 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.176 + 4.8.177-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.176 + classgraph-4.8.175 From dcd7ece45e4de71e9b174852e1f1d603dd66acd3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Oct 2024 20:22:31 -0600 Subject: [PATCH 678/713] Don't allow resources to be opened after ScanResult is closed (#885) --- .../io/github/classgraph/ClassMemberInfo.java | 3 +- .../github/classgraph/ClasspathElement.java | 10 ++ .../classgraph/ClasspathElementDir.java | 30 ++-- .../classgraph/ClasspathElementModule.java | 24 +-- .../classgraph/ClasspathElementZip.java | 41 ++--- .../java/io/github/classgraph/ScanResult.java | 5 + .../fastzipfilereader/NestedJarHandler.java | 151 +++++++----------- 7 files changed, 120 insertions(+), 144 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassMemberInfo.java b/src/main/java/io/github/classgraph/ClassMemberInfo.java index 84b0bfaad..c0d71ab1c 100644 --- a/src/main/java/io/github/classgraph/ClassMemberInfo.java +++ b/src/main/java/io/github/classgraph/ClassMemberInfo.java @@ -287,8 +287,9 @@ public String getTypeSignatureOrTypeDescriptorStr() { */ public AnnotationInfoList getAnnotationInfo() { synchronized (this) { - if (annotationInfoRef != null) + if (annotationInfoRef != null) { return annotationInfoRef; + } if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index ebb91f010..84fe51126 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -117,6 +117,9 @@ abstract class ClasspathElement implements Comparable { /** The scan spec. */ final ScanSpec scanSpec; + /** The ScanResult that the classpath element came from. */ + protected ScanResult scanResult; + // ------------------------------------------------------------------------------------------------------------- /** @@ -136,6 +139,13 @@ abstract class ClasspathElement implements Comparable { // ------------------------------------------------------------------------------------------------------------- + /** Used to set the ScanResult after the scan is complete. */ + void setScanResult(final ScanResult scanResult) { + this.scanResult = scanResult; + } + + // ------------------------------------------------------------------------------------------------------------- + /** Sort in increasing order of classpathElementIdxWithinParent. */ @Override public int compareTo(final ClasspathElement other) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 8a6df87b4..8a0707998 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -227,9 +227,22 @@ public Set getPosixFilePermissions() { return posixFilePermissions; } + protected void checkCanOpen() { + if (skipClasspathElement) { + // Shouldn't happen + throw new IllegalStateException("Classpath element could not be opened"); + } + if (isOpen.getAndSet(true)) { + throw new IllegalStateException( + "Resource is already open -- cannot open it again without first calling close()"); + } + if (scanResult.isClosed()) { + throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); + } + } + @Override public ByteBuffer read() throws IOException { - checkSkipState(); openAndCreateSlice(); byteBuffer = pathSlice.read(); return byteBuffer; @@ -237,7 +250,6 @@ public ByteBuffer read() throws IOException { @Override ClassfileReader openClassfile() throws IOException { - checkSkipState(); // Classfile won't be compressed, so wrap it in a new PathSlice and then open it openAndCreateSlice(); return new ClassfileReader(pathSlice, this); @@ -245,7 +257,6 @@ ClassfileReader openClassfile() throws IOException { @Override public InputStream open() throws IOException { - checkSkipState(); openAndCreateSlice(); inputStream = pathSlice.open(this); return inputStream; @@ -253,7 +264,6 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { - checkSkipState(); try { openAndCreateSlice(); return pathSlice.load(); @@ -280,18 +290,8 @@ public void close() { } } - private void checkSkipState() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - } - private void openAndCreateSlice() throws IOException { - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkCanOpen(); pathSlice = new PathSlice(resourcePath, false, 0L, nestedJarHandler, false); length = pathSlice.sliceLength; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 6af3660c7..0aae81f50 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -154,16 +154,23 @@ public Set getPosixFilePermissions() { return null; // N/A } - @Override - public ByteBuffer read() throws IOException { + protected void checkCanOpen() { if (skipClasspathElement) { // Shouldn't happen - throw new IOException("Module could not be opened"); + throw new IllegalStateException("Classpath element could not be opened"); } if (isOpen.getAndSet(true)) { - throw new IOException( + throw new IllegalStateException( "Resource is already open -- cannot open it again without first calling close()"); } + if (scanResult.isClosed()) { + throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); + } + } + + @Override + public ByteBuffer read() throws IOException { + checkCanOpen(); try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); // ModuleReader#read(String name) internally calls: @@ -199,14 +206,7 @@ public URI getURI() { @Override public InputStream open() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Module could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkCanOpen(); try { final Resource thisResource = this; moduleReaderProxy = moduleReaderProxyRecycler.acquire(); diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7393ffa68..0032ad6f3 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -371,6 +371,20 @@ public Set getPosixFilePermissions() { return perms; } + protected void checkCanOpen() { + if (skipClasspathElement) { + // Shouldn't happen + throw new IllegalStateException("Classpath element could not be opened"); + } + if (isOpen.getAndSet(true)) { + throw new IllegalStateException( + "Resource is already open -- cannot open it again without first calling close()"); + } + if (scanResult.isClosed()) { + throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); + } + } + @Override ClassfileReader openClassfile() throws IOException { return new ClassfileReader(open(), this); @@ -378,14 +392,7 @@ ClassfileReader openClassfile() throws IOException { @Override public InputStream open() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Jarfile could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkCanOpen(); try { inputStream = zipEntry.getSlice().open(this); length = zipEntry.uncompressedSize; @@ -399,14 +406,7 @@ public InputStream open() throws IOException { @Override public ByteBuffer read() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Jarfile could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkCanOpen(); try { byteBuffer = zipEntry.getSlice().read(); length = byteBuffer.remaining(); @@ -419,14 +419,7 @@ public ByteBuffer read() throws IOException { @Override public byte[] load() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Jarfile could not be opened"); - } - if (isOpen.getAndSet(true)) { - throw new IOException( - "Resource is already open -- cannot open it again without first calling close()"); - } + checkCanOpen(); try (Resource res = this) { // Close this after use final byte[] byteArray = zipEntry.getSlice().load(); res.length = byteArray.length; diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 603593818..7a2eabce6 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1712,6 +1712,11 @@ public void close() { } } + /** Returns whether this ScanResult has been closed yet or not. */ + public boolean isClosed() { + return closed.get(); + } + /** * Close all {@link ScanResult} instances that have not yet been closed. Note that this will close all open * {@link ScanResult} instances for any class that uses the classloader that the {@link ScanResult} class is diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index e8cef97a9..393d9e5fd 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -87,10 +87,8 @@ public class NestedJarHandler { public ReflectionUtils reflectionUtils; /** - * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} - * for that file, used to ensure - * that the {@link RandomAccessFile} and {@link FileChannel} for any given - * zipfile is opened only once. + * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} for that file, used to ensure + * that the {@link RandomAccessFile} and {@link FileChannel} for any given zipfile is opened only once. */ private SingletonMap // canonicalFileToPhysicalZipFileMap = new SingletonMap() { @@ -101,10 +99,8 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) }; /** - * A singleton map from a {@link FastZipEntry} to the {@link ZipFileSlice} - * wrapping either the zip entry data, - * if the entry is stored, or a ByteBuffer, if the zip entry was inflated to - * memory, or a physical file on disk + * A singleton map from a {@link FastZipEntry} to the {@link ZipFileSlice} wrapping either the zip entry data, + * if the entry is stored, or a ByteBuffer, if the zip entry was inflated to memory, or a physical file on disk * if the zip entry was inflated to a temporary file. */ private SingletonMap // @@ -145,8 +141,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode }; /** - * A singleton map from a {@link ZipFileSlice} to the {@link LogicalZipFile} for - * that slice. + * A singleton map from a {@link ZipFileSlice} to the {@link LogicalZipFile} for that slice. */ private SingletonMap // zipFileSliceToLogicalZipFileMap = new SingletonMap() { @@ -160,8 +155,7 @@ public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode }; /** - * A singleton map from nested jarfile path to a tuple of the logical zipfile - * for the path, and the package root + * A singleton map from nested jarfile path to a tuple of the logical zipfile for the path, and the package root * within the logical zipfile. */ public SingletonMap, IOException> // @@ -373,8 +367,7 @@ public Entry newInstance(final String nestedJarPathRaw, }; /** - * A singleton map from a {@link ModuleRef} to a {@link ModuleReaderProxy} - * recycler for the module. + * A singleton map from a {@link ModuleRef} to a {@link ModuleReaderProxy} recycler for the module. */ public SingletonMap, IOException> // moduleRefToModuleReaderProxyRecyclerMap = // @@ -430,9 +423,9 @@ public RecyclableInflater newInstance() throws RuntimeException { * A handler for nested jars. * * @param scanSpec - * The {@link ScanSpec}. + * The {@link ScanSpec}. * @param interruptionChecker - * the interruption checker + * the interruption checker */ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker interruptionChecker, final ReflectionUtils reflectionUtils) { @@ -447,7 +440,7 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter * Get the leafname of a path. * * @param path - * the path + * the path * @return the string */ private static String leafname(final String path) { @@ -458,7 +451,7 @@ private static String leafname(final String path) { * Sanitize filename. * * @param filename - * the filename + * the filename * @return the sanitized filename */ private String sanitizeFilename(final String filename) { @@ -470,13 +463,12 @@ private String sanitizeFilename(final String filename) { * Create a temporary file, and mark it for deletion on exit. * * @param filePathBase - * The path to derive the temporary filename from. + * The path to derive the temporary filename from. * @param onlyUseLeafname - * If true, only use the leafname of filePath to derive - * the temporary filename. + * If true, only use the leafname of filePath to derive the temporary filename. * @return The temporary {@link File}. * @throws IOException - * If the temporary file could not be created. + * If the temporary file could not be created. */ public File makeTempFile(final String filePathBase, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR @@ -490,11 +482,11 @@ public File makeTempFile(final String filePathBase, final boolean onlyUseLeafnam * Attempt to remove a temporary file. * * @param tempFile - * the temp file + * the temp file * @throws IOException - * If the temporary file could not be removed. + * If the temporary file could not be removed. * @throws SecurityException - * If the temporary file is inaccessible. + * If the temporary file is inaccessible. */ void removeTempFile(final File tempFile) throws IOException, SecurityException { if (tempFiles.remove(tempFile)) { @@ -505,13 +497,12 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { } /** - * Mark a {@link Slice} as open, so it can be closed when the {@link ScanResult} - * is closed. + * Mark a {@link Slice} as open, so it can be closed when the {@link ScanResult} is closed. * * @param slice - * the {@link Slice} that was just opened. + * the {@link Slice} that was just opened. * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ public void markSliceAsOpen(final Slice slice) throws IOException { openSlices.add(slice); @@ -521,39 +512,30 @@ public void markSliceAsOpen(final Slice slice) throws IOException { * Mark a {@link Slice} as closed. * * @param slice - * the {@link Slice} to close. + * the {@link Slice} to close. */ public void markSliceAsClosed(final Slice slice) { openSlices.remove(slice); } /** - * Download a jar from a URL to a temporary file, or to a ByteBuffer if the - * temporary directory is not writeable - * or full. The downloaded jar is returned wrapped in a {@link PhysicalZipFile} - * instance. + * Download a jar from a URL to a temporary file, or to a ByteBuffer if the temporary directory is not writeable + * or full. The downloaded jar is returned wrapped in a {@link PhysicalZipFile} instance. * * @param jarURL - * the jar URL + * the jar URL * @param log - * the log - * @return the temporary file or {@link ByteBuffer} the jar was downloaded to, - * wrapped in a + * the log + * @return the temporary file or {@link ByteBuffer} the jar was downloaded to, wrapped in a * {@link PhysicalZipFile} instance. * @throws IOException - * If the jar could not be downloaded, or the - * jar URL is malformed. + * If the jar could not be downloaded, or the jar URL is malformed. * @throws InterruptedException - * if the thread was interrupted + * if the thread was interrupted * @throws IllegalArgumentException - * If the temp dir is not writeable, or has - * insufficient space to download the jar. - * (This is thrown - * as a separate exception from IOException, so - * that the case of an unwriteable temp dir can - * be - * handled separately, by downloading the jar - * to a ByteBuffer in RAM.) + * If the temp dir is not writeable, or has insufficient space to download the jar. (This is thrown + * as a separate exception from IOException, so that the case of an unwriteable temp dir can be + * handled separately, by downloading the jar to a ByteBuffer in RAM.) */ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) throws IOException, InterruptedException { @@ -660,14 +642,12 @@ public void close() { // ------------------------------------------------------------------------------------------------------------- /** - * Wrapper class that allows an {@link Inflater} instance to be reset for reuse - * and then recycled by a + * Wrapper class that allows an {@link Inflater} instance to be reset for reuse and then recycled by a * {@link Recycler}. */ private static class RecyclableInflater implements Resettable, AutoCloseable { /** - * Create a new {@link Inflater} instance with the "nowrap" option (which is - * needed for zipfile entries). + * Create a new {@link Inflater} instance with the "nowrap" option (which is needed for zipfile entries). */ private final Inflater inflater = new Inflater(/* nowrap = */ true); @@ -681,8 +661,7 @@ public Inflater getInflater() { } /** - * Called when an {@link Inflater} instance is recycled, to reset the inflater - * so it can accept new input. + * Called when an {@link Inflater} instance is recycled, to reset the inflater so it can accept new input. */ @Override public void reset() { @@ -690,8 +669,7 @@ public void reset() { } /** - * Called when the {@link Recycler} instance is closed, to destroy the - * {@link Inflater} instance. + * Called when the {@link Recycler} instance is closed, to destroy the {@link Inflater} instance. */ @Override public void close() { @@ -700,14 +678,13 @@ public void close() { } /** - * Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling - * the {@link Inflater} instance. + * Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling the {@link Inflater} instance. * * @param rawInputStream - * the raw input stream + * the raw input stream * @return the inflater input stream * @throws IOException - * Signals that an I/O exception has occurred. + * Signals that an I/O exception has occurred. */ public InputStream openInflaterInputStream(final InputStream rawInputStream) throws IOException { return new InputStream() { @@ -853,28 +830,24 @@ public void close() { // ------------------------------------------------------------------------------------------------------------- /** - * Read all the bytes in an {@link InputStream}, with spillover to a temporary - * file on disk if a maximum buffer + * Read all the bytes in an {@link InputStream}, with spillover to a temporary file on disk if a maximum buffer * size is exceeded. * * @param inputStream - * the {@link InputStream} to read from. + * the {@link InputStream} to read from. * @param tempFileBaseName - * the source URL or zip entry that inputStream was - * opened from (used to name temporary file, if - * needed). + * the source URL or zip entry that inputStream was opened from (used to name temporary file, if + * needed). * @param inputStreamLengthHint - * the length of inputStream if known, else -1L. + * the length of inputStream if known, else -1L. * @param log - * the log. - * @return if the {@link InputStream} could be read into a byte array, an - * {@link ArraySlice} will be returned. - * If this fails and the {@link InputStream} is spilled over to disk, a - * {@link FileSlice} will be + * the log. + * @return if the {@link InputStream} could be read into a byte array, an {@link ArraySlice} will be returned. + * If this fails and the {@link InputStream} is spilled over to disk, a {@link FileSlice} will be * returned. * * @throws IOException - * If the contents could not be read. + * If the contents could not be read. */ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, final String tempFileBaseName, final long inputStreamLengthHint, final LogNode log) throws IOException { @@ -937,26 +910,22 @@ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, fina } /** - * Spill an {@link InputStream} to disk if the stream is too large to fit in - * RAM. + * Spill an {@link InputStream} to disk if the stream is too large to fit in RAM. * * @param inputStream - * The {@link InputStream}. + * The {@link InputStream}. * @param tempFileBaseName - * The stem to base the temporary filename on. + * The stem to base the temporary filename on. * @param buf - * The first buffer to write to the beginning of the - * file, or null if none. + * The first buffer to write to the beginning of the file, or null if none. * @param overflowBuf - * The second buffer to write to the beginning of the - * file, or null if none. (Should have same - * nullity as buf.) + * The second buffer to write to the beginning of the file, or null if none. (Should have same + * nullity as buf.) * @param log - * The log. + * The log. * @return the file slice * @throws IOException - * If anything went wrong creating or writing to the temp - * file. + * If anything went wrong creating or writing to the temp file. */ private FileSlice spillToDisk(final InputStream inputStream, final String tempFileBaseName, final byte[] buf, final byte[] overflowBuf, final LogNode log) throws IOException { @@ -995,13 +964,12 @@ private FileSlice spillToDisk(final InputStream inputStream, final String tempFi * Read all the bytes in an {@link InputStream}. * * @param inputStream - * The {@link InputStream}. + * The {@link InputStream}. * @param uncompressedLengthHint - * The length of the data once inflated from the - * {@link InputStream}, if known, otherwise -1L. + * The length of the data once inflated from the {@link InputStream}, if known, otherwise -1L. * @return The contents of the {@link InputStream} as a byte array. * @throws IOException - * If the contents could not be read. + * If the contents could not be read. */ public static byte[] readAllBytesAsArray(final InputStream inputStream, final long uncompressedLengthHint) throws IOException { @@ -1056,8 +1024,7 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo // ------------------------------------------------------------------------------------------------------------- /** - * Close zipfiles, modules, and recyclers, and delete temporary files. Called by - * {@link ScanResult#close()}. + * Close zipfiles, modules, and recyclers, and delete temporary files. Called by {@link ScanResult#close()}. * * @param log * The log. From a1386412fe3a5ea0588168a7956ce722fbccd968 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Oct 2024 20:32:02 -0600 Subject: [PATCH 679/713] Fix previous commit --- pom.xml | 4 ++-- .../io/github/classgraph/ClasspathElementDir.java | 2 +- .../github/classgraph/ClasspathElementModule.java | 2 +- .../io/github/classgraph/ClasspathElementZip.java | 2 +- src/main/java/io/github/classgraph/Scanner.java | 14 +++++++++++--- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 039340989..647a70308 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.177-SNAPSHOT + 4.8.177 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.175 + classgraph-4.8.177 diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 8a0707998..1468542e6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -236,7 +236,7 @@ protected void checkCanOpen() { throw new IllegalStateException( "Resource is already open -- cannot open it again without first calling close()"); } - if (scanResult.isClosed()) { + if (scanResult != null && scanResult.isClosed()) { throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 0aae81f50..734638057 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -163,7 +163,7 @@ protected void checkCanOpen() { throw new IllegalStateException( "Resource is already open -- cannot open it again without first calling close()"); } - if (scanResult.isClosed()) { + if (scanResult != null && scanResult.isClosed()) { throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 0032ad6f3..83649ad54 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -380,7 +380,7 @@ protected void checkCanOpen() { throw new IllegalStateException( "Resource is already open -- cannot open it again without first calling close()"); } - if (scanResult.isClosed()) { + if (scanResult != null && scanResult.isClosed()) { throw new IllegalStateException("Cannot open a resource after the ScanResult is closed"); } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index b0a8dd5df..d0f81c76c 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -1019,9 +1019,17 @@ private ScanResult performScan(final List finalClasspathEltOrd } // Return a new ScanResult - return new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, classpathFinder, - classNameToClassInfo, packageNameToPackageInfo, moduleNameToModuleInfo, fileToLastModified, - nestedJarHandler, topLevelLog); + final ScanResult scanResult = new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, + classpathFinder, classNameToClassInfo, packageNameToPackageInfo, moduleNameToModuleInfo, + fileToLastModified, nestedJarHandler, topLevelLog); + + // Set the ScanResult in each classpath element, so that the classpath elements can determine when the + // ScanResult is closed + for (final ClasspathElement classpathElement : finalClasspathEltOrder) { + classpathElement.setScanResult(scanResult); + } + + return scanResult; } // ------------------------------------------------------------------------------------------------------------- From e5422fbd851d7d716b5c4c0c0923f1918b1165d4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Oct 2024 20:34:10 -0600 Subject: [PATCH 680/713] Reset version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 647a70308..d349a9435 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.177 + 4.8.177-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 8841196f2ad99bd8314a94c335f66026d2eabec4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Oct 2024 20:34:31 -0600 Subject: [PATCH 681/713] [maven-release-plugin] prepare release classgraph-4.8.177 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d349a9435..647a70308 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.177-SNAPSHOT + 4.8.177 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 455a40a845f4bd6bef34d509c442350f2088b006 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Oct 2024 20:34:33 -0600 Subject: [PATCH 682/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 647a70308..ccba36f81 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.177 + 4.8.178-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 68b3a5319a0fb1efce765d1c59fc5052ea559fc4 Mon Sep 17 00:00:00 2001 From: Gili Tzabari Date: Sat, 19 Oct 2024 23:24:26 -0400 Subject: [PATCH 683/713] Fixes #888: ClasspathElementZip.getClasspathElementURI() returns incorrect URIs under Windows. * Use file:/// for absolute paths and file: for relative paths. * Added "Test" suffix to unit tests that were being skipped by Surefire. --- pom.xml | 2 - .../java/io/github/classgraph/ScanResult.java | 5 +- .../classgraph/utils/URLPathEncoder.java | 20 ++-- .../{Issue289.java => Issue289Test.java} | 6 +- .../classgraph/issues/issue305/Issue305.java | 90 ------------------ .../issues/issue305/Issue305Test.java | 90 ++++++++++++++++++ .../{Issue310.java => Issue310Test.java} | 10 +- .../{Issue314.java => Issue314Test.java} | 8 +- .../{Issue318.java => Issue318Test.java} | 4 +- .../{Issue329.java => Issue329Test.java} | 4 +- .../{Issue339.java => Issue339Test.java} | 2 +- .../{Issue340.java => Issue340Test.java} | 2 +- .../{Issue345.java => Issue345Test.java} | 16 ++-- .../{Issue348.java => Issue348Test.java} | 2 +- .../{Issue350.java => Issue350Test.java} | 6 +- .../{Issue352.java => Issue352Test.java} | 4 +- .../{Issue355.java => Issue355Test.java} | 4 +- .../{Issue694.java => Issue694Test.java} | 8 +- .../{Issue696.java => Issue696Test.java} | 6 +- .../{Issue706.java => Issue706Test.java} | 4 +- .../{Issue735.java => Issue735Test.java} | 6 +- .../{Issue739.java => Issue739Test.java} | 2 +- ...ssgraphIssue766.java => Issue766Test.java} | 4 +- .../{Issue93.java => Issue93Test.java} | 4 +- .../resources/class-path-manifest-entry.jar | Bin 625 -> 631 bytes 25 files changed, 157 insertions(+), 152 deletions(-) rename src/test/java/io/github/classgraph/issues/issue289/{Issue289.java => Issue289Test.java} (80%) delete mode 100644 src/test/java/io/github/classgraph/issues/issue305/Issue305.java create mode 100644 src/test/java/io/github/classgraph/issues/issue305/Issue305Test.java rename src/test/java/io/github/classgraph/issues/issue310/{Issue310.java => Issue310Test.java} (78%) rename src/test/java/io/github/classgraph/issues/issue314/{Issue314.java => Issue314Test.java} (84%) rename src/test/java/io/github/classgraph/issues/issue318/{Issue318.java => Issue318Test.java} (95%) rename src/test/java/io/github/classgraph/issues/issue329/{Issue329.java => Issue329Test.java} (92%) rename src/test/java/io/github/classgraph/issues/issue339/{Issue339.java => Issue339Test.java} (98%) rename src/test/java/io/github/classgraph/issues/issue340/{Issue340.java => Issue340Test.java} (98%) rename src/test/java/io/github/classgraph/issues/issue345/{Issue345.java => Issue345Test.java} (91%) rename src/test/java/io/github/classgraph/issues/issue348/{Issue348.java => Issue348Test.java} (98%) rename src/test/java/io/github/classgraph/issues/issue350/{Issue350.java => Issue350Test.java} (95%) rename src/test/java/io/github/classgraph/issues/issue352/{Issue352.java => Issue352Test.java} (96%) rename src/test/java/io/github/classgraph/issues/issue355/{Issue355.java => Issue355Test.java} (95%) rename src/test/java/io/github/classgraph/issues/issue694/{Issue694.java => Issue694Test.java} (85%) rename src/test/java/io/github/classgraph/issues/issue696/{Issue696.java => Issue696Test.java} (91%) rename src/test/java/io/github/classgraph/issues/issue706/{Issue706.java => Issue706Test.java} (93%) rename src/test/java/io/github/classgraph/issues/issue735/{Issue735.java => Issue735Test.java} (92%) rename src/test/java/io/github/classgraph/issues/issue739/{Issue739.java => Issue739Test.java} (97%) rename src/test/java/io/github/classgraph/issues/issue766/{ClassgraphIssue766.java => Issue766Test.java} (90%) rename src/test/java/io/github/classgraph/issues/issue93/{Issue93.java => Issue93Test.java} (95%) diff --git a/pom.xml b/pom.xml index ccba36f81..c000b45c6 100644 --- a/pom.xml +++ b/pom.xml @@ -565,8 +565,6 @@ org.apache.maven.plugins maven-release-plugin - deploy - deploy true release diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 7a2eabce6..52fe67e8b 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1712,7 +1712,10 @@ public void close() { } } - /** Returns whether this ScanResult has been closed yet or not. */ + /** + * Returns whether this ScanResult has been closed yet or not. + * @return {@code true} if this ScanResult has been closed + */ public boolean isClosed() { return closed.get(); } diff --git a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java index 5ac5eb4d8..28ed7bb8f 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -154,8 +154,8 @@ public static String encodePath(final String path) { // Also accept ':' after a Windows drive letter if (VersionFinder.OS == OperatingSystem.Windows) { int i = validColonPrefixLen; - if (i < path.length() && path.charAt(i) == '/') { - i++; + if (i < path.length() && path.startsWith("///", i)) { + i += "///".length(); } if (i < path.length() - 1 && Character.isLetter(path.charAt(i)) && path.charAt(i + 1) == ':') { validColonPrefixLen = i + 2; @@ -219,15 +219,19 @@ public static String normalizeURLPath(final String urlPath) { // Any URL containing "!" segments must have "/" after "!" for the "jar:" URL scheme to work urlPathNormalized = urlPathNormalized.replace("/!", "!").replace("!/", "!").replace("!", "!/"); - // Prepend "file:/" + // Prepend "file:///" to absolute paths and "file:" to relative paths if (windowsDrivePrefix.isEmpty()) { // There is no Windows drive - urlPathNormalized = urlPathNormalized.startsWith("/") ? "file:" + urlPathNormalized - : "file:/" + urlPathNormalized; + if (urlPathNormalized.startsWith("/")) { + // Absolute path: file:///xyz + urlPathNormalized = "file://" + urlPathNormalized; + } else { + // Relative path: file:xyz + urlPathNormalized = "file:" + urlPathNormalized; + } } else { - // There is a Windows drive - urlPathNormalized = "file:/" + windowsDrivePrefix - + (urlPathNormalized.startsWith("/") ? urlPathNormalized : "/" + urlPathNormalized); + // There is a Windows drive, path must be absolute + urlPathNormalized = "file:///" + windowsDrivePrefix + urlPathNormalized; } // Prepend "jar:" if path contains a "!" segment diff --git a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java b/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java similarity index 80% rename from src/test/java/io/github/classgraph/issues/issue289/Issue289.java rename to src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java index 744448a03..ae875e683 100644 --- a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java +++ b/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java @@ -14,7 +14,7 @@ /** * Issue289. */ -public class Issue289 { +public class Issue289Test { /** * Issue 289. @@ -23,10 +23,10 @@ public class Issue289 { * the exception */ @Test - public void issue289() throws Exception { + public void issue289() { try (ScanResult scanResult = new ClassGraph() .overrideClassLoaders( - new URLClassLoader(new URL[] { Issue289.class.getClassLoader().getResource("zip64.zip") })) + new URLClassLoader(new URL[] { Issue289Test.class.getClassLoader().getResource("zip64.zip") })) .scan()) { for (int i = 0; i < 90000; i++) { final ResourceList resources = scanResult.getResourcesWithPath(i + ""); diff --git a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java deleted file mode 100644 index 353ca8175..000000000 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.classgraph.issues.issue305; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; - -/** - * Issue305. - */ -public class Issue305 { - private ConsoleHandler errPrintStreamHandler = null; - private final Logger rootLogger = Logger.getLogger(""); - - /** Reset encapsulation circumvention method after each test. */ - @AfterEach - void resetAfterTest() { - rootLogger.removeHandler(errPrintStreamHandler); - // Set to System.err - System.setErr(System.err); - } - - /** - * Test that multi-line continuations in manifest file values are correctly assembled into a string. - * - * @throws Exception - * the exception - */ - @Test - public void issue305() throws Exception { - // Record log output - final ByteArrayOutputStream err = new ByteArrayOutputStream(); - System.setErr(new PrintStream(err)); - final Logger log = Logger.getLogger(ClassGraph.class.getName()); - if (!log.isLoggable(Level.INFO)) { - throw new Exception("Could not create log"); - } - errPrintStreamHandler = new ConsoleHandler(); - errPrintStreamHandler.setLevel(Level.INFO); - rootLogger.addHandler(errPrintStreamHandler); - - try (ScanResult scanResult = new ClassGraph() - .overrideClassLoaders(new URLClassLoader( - new URL[] { Issue305.class.getClassLoader().getResource("class-path-manifest-entry.jar") })) - // This .verbose() is needed (stderr is captured) - .verbose().scan()) { - } - - final String systemErrMessages = new String(err.toByteArray()); - assertThat(systemErrMessages.indexOf("Found Class-Path entry in manifest file: " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/charsets.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/deploy.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/access-bridge-64.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/cldrdata.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/dnsns.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jaccess.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jfxrt.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/localedata.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/nashorn.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunec.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunjce_provider.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunmscapi.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunpkcs11.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/zipfs.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/javaws.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jce.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfr.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfxswt.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jsse.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/management-agent.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/plugin.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/resources.jar " - + "file:/C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/rt.jar " - + "file:/Z:/classgraphtest/target/classes/ " - + "file:/C:/Users/flame/.m2/repository/io/github/classgraph/classgraph/4.6.19/classgraph-4.6.19.jar " - + "file:/C:/Program%20Files/JetBrains/IntelliJ%20IDEA%20Community%20Edition%202018.2.1/lib/idea_rt.jar")) - .isGreaterThan(0); - } -} diff --git a/src/test/java/io/github/classgraph/issues/issue305/Issue305Test.java b/src/test/java/io/github/classgraph/issues/issue305/Issue305Test.java new file mode 100644 index 000000000..b8e033592 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305Test.java @@ -0,0 +1,90 @@ +package io.github.classgraph.issues.issue305; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue305. + */ +public class Issue305Test { + private ConsoleHandler errPrintStreamHandler = null; + private final Logger rootLogger = Logger.getLogger(""); + + /** Reset encapsulation circumvention method after each test. */ + @AfterEach + void resetAfterTest() { + rootLogger.removeHandler(errPrintStreamHandler); + // Set to System.err + System.setErr(System.err); + } + + /** + * Test that multi-line continuations in manifest file values are correctly assembled into a string. + * + * @throws Exception + * the exception + */ + @Test + public void issue305() throws Exception { + // Record log output + final ByteArrayOutputStream err = new ByteArrayOutputStream(); + System.setErr(new PrintStream(err)); + final Logger log = Logger.getLogger(ClassGraph.class.getName()); + if (!log.isLoggable(Level.INFO)) { + throw new Exception("Could not create log"); + } + errPrintStreamHandler = new ConsoleHandler(); + errPrintStreamHandler.setLevel(Level.INFO); + rootLogger.addHandler(errPrintStreamHandler); + + try (ScanResult scanResult = new ClassGraph() + .overrideClassLoaders(new URLClassLoader( + new URL[] { Issue305Test.class.getClassLoader().getResource("class-path-manifest-entry.jar") })) + // This .verbose() is needed (stderr is captured) + .verbose().scan()) { + } + + final String systemErrMessages = new String(err.toByteArray()); + assertThat(systemErrMessages.indexOf("Found Class-Path entry in manifest file: " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/charsets.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/deploy.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/access-bridge-64.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/cldrdata.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/dnsns.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jaccess.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/jfxrt.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/localedata.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/nashorn.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunec.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunjce_provider.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunmscapi.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/sunpkcs11.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/ext/zipfs.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/javaws.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jce.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfr.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jfxswt.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/jsse.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/management-agent.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/plugin.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/resources.jar " + + "file:///C:/Program%20Files/Java/jdk1.8.0_162/jre/lib/rt.jar " + + "file:///Z:/classgraphtest/target/classes/ " + + "file:///C:/Users/flame/.m2/repository/io/github/classgraph/classgraph/4.6.19/classgraph-4.6.19.jar " + + "file:///C:/Program%20Files/JetBrains/IntelliJ%20IDEA%20Community%20Edition%202018.2.1/lib/idea_rt.jar")) + .isGreaterThan(0); + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java b/src/test/java/io/github/classgraph/issues/issue310/Issue310Test.java similarity index 78% rename from src/test/java/io/github/classgraph/issues/issue310/Issue310.java rename to src/test/java/io/github/classgraph/issues/issue310/Issue310Test.java index f8048b768..4602a8372 100644 --- a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java +++ b/src/test/java/io/github/classgraph/issues/issue310/Issue310Test.java @@ -10,7 +10,7 @@ /** * Issue310. */ -public class Issue310 { +public class Issue310Test { /** The Constant A. */ static final double A = 3.0; @@ -35,12 +35,12 @@ public void issue310() { // the same after the first and second deserialization, because overrideClasspath is set by the first // serialization for consistency.) final String classfileURL = getClass().getClassLoader() - .getResource(Issue310.class.getName().replace('.', '/') + ".class").toString(); + .getResource(Issue310Test.class.getName().replace('.', '/') + ".class").toString(); final String classpathBase = classfileURL.substring(0, - classfileURL.length() - (Issue310.class.getName().length() + 6)); + classfileURL.length() - (Issue310Test.class.getName().length() + 6)); try (ScanResult scanResult1 = new ClassGraph().overrideClasspath(classpathBase) - .acceptClasses(Issue310.class.getName()).enableAllInfo().scan()) { - assertThat(scanResult1.getClassInfo(Issue310.class.getName()).getFieldInfo("B")).isNotNull(); + .acceptClasses(Issue310Test.class.getName()).enableAllInfo().scan()) { + assertThat(scanResult1.getClassInfo(Issue310Test.class.getName()).getFieldInfo("B")).isNotNull(); final String json1 = scanResult1.toJSON(2); assertThat(json1).isNotEmpty(); try (ScanResult scanResult2 = ScanResult.fromJSON(scanResult1.toJSON())) { diff --git a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java b/src/test/java/io/github/classgraph/issues/issue314/Issue314Test.java similarity index 84% rename from src/test/java/io/github/classgraph/issues/issue314/Issue314.java rename to src/test/java/io/github/classgraph/issues/issue314/Issue314Test.java index 97727bc6a..e1056b89f 100644 --- a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java +++ b/src/test/java/io/github/classgraph/issues/issue314/Issue314Test.java @@ -10,7 +10,7 @@ /** * Issue314. */ -public class Issue314 { +public class Issue314Test { /** * The Class A. */ @@ -32,11 +32,11 @@ public void issue314() { // the same after the first and second deserialization, because overrideClasspath is set by the first // serialization for consistency.) final String classfileURL = getClass().getClassLoader() - .getResource(Issue314.class.getName().replace('.', '/') + ".class").toString(); + .getResource(Issue314Test.class.getName().replace('.', '/') + ".class").toString(); final String classpathBase = classfileURL.substring(0, - classfileURL.length() - (Issue314.class.getName().length() + 6)); + classfileURL.length() - (Issue314Test.class.getName().length() + 6)); try (ScanResult scanResult1 = new ClassGraph().overrideClasspath(classpathBase) - .acceptPackages(Issue314.class.getPackage().getName()).enableAllInfo().scan()) { + .acceptPackages(Issue314Test.class.getPackage().getName()).enableAllInfo().scan()) { assertThat(scanResult1.getClassInfo(A.class.getName())).isNotNull(); assertThat(scanResult1.getClassInfo(B.class.getName())).isNotNull(); final String json1 = scanResult1.toJSON(2); diff --git a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java b/src/test/java/io/github/classgraph/issues/issue318/Issue318Test.java similarity index 95% rename from src/test/java/io/github/classgraph/issues/issue318/Issue318.java rename to src/test/java/io/github/classgraph/issues/issue318/Issue318Test.java index ae356e26d..1ff94c85c 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318Test.java @@ -16,7 +16,7 @@ /** * Unit test. */ -public class Issue318 { +public class Issue318Test { /** * The Interface MyAnn. */ @@ -75,7 +75,7 @@ class With3MyAnn { */ @Test public void issue318() { - try (final ScanResult scanResult = new ClassGraph().acceptPackages(Issue318.class.getPackage().getName()) + try (final ScanResult scanResult = new ClassGraph().acceptPackages(Issue318Test.class.getPackage().getName()) .enableAnnotationInfo().enableClassInfo().ignoreClassVisibility() // //.verbose() // .scan()) { diff --git a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java b/src/test/java/io/github/classgraph/issues/issue329/Issue329Test.java similarity index 92% rename from src/test/java/io/github/classgraph/issues/issue329/Issue329.java rename to src/test/java/io/github/classgraph/issues/issue329/Issue329Test.java index 25b19ed10..1c2c1d895 100644 --- a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329Test.java @@ -11,7 +11,7 @@ /** * Unit test. */ -public class Issue329 { +public class Issue329Test { /** The Class Foo. */ public class Foo { /** Constructor. */ @@ -30,7 +30,7 @@ public void test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies() .enableExternalClasses().acceptClasses(Foo.class.getName()).scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Foo.class.getName()); - assertThat(classInfo.getClassDependencies().getNames()).containsOnly(Issue329.class.getName(), + assertThat(classInfo.getClassDependencies().getNames()).containsOnly(Issue329Test.class.getName(), Bar.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java b/src/test/java/io/github/classgraph/issues/issue339/Issue339Test.java similarity index 98% rename from src/test/java/io/github/classgraph/issues/issue339/Issue339.java rename to src/test/java/io/github/classgraph/issues/issue339/Issue339Test.java index d4cb248cc..b9c864c31 100644 --- a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java +++ b/src/test/java/io/github/classgraph/issues/issue339/Issue339Test.java @@ -18,7 +18,7 @@ /** * Unit test. */ -public class Issue339 { +public class Issue339Test { /** * Grade. */ diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340Test.java similarity index 98% rename from src/test/java/io/github/classgraph/issues/issue340/Issue340.java rename to src/test/java/io/github/classgraph/issues/issue340/Issue340Test.java index 113d49fe0..52b836d54 100644 --- a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340Test.java @@ -16,7 +16,7 @@ /** * Unit test. */ -public class Issue340 { +public class Issue340Test { /** Test. */ @Test public void test() { diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java similarity index 91% rename from src/test/java/io/github/classgraph/issues/issue345/Issue345.java rename to src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java index ee8ee9a3c..ae8a82129 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java @@ -15,7 +15,7 @@ /** * Issue345. */ -public class Issue345 { +public class Issue345Test { /** * Superclass. */ @@ -81,7 +81,7 @@ public void testExtensionToParent() { public void testExtensionToOuterClass() { try (ScanResult scanResult = new ClassGraph().acceptClasses(Super.class.getName()).ignoreClassVisibility() .scan()) { - final ClassInfo outerClassInfo = scanResult.getClassInfo(Issue345.class.getName()); + final ClassInfo outerClassInfo = scanResult.getClassInfo(Issue345Test.class.getName()); assertThat(outerClassInfo).isNotNull(); assertThat(outerClassInfo.getResource()).isNotNull(); } @@ -92,7 +92,7 @@ public void testExtensionToOuterClass() { */ @Test public void testNonExtensionToInnerClass() { - try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345.class.getName()) + try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345Test.class.getName()) .ignoreClassVisibility().scan()) { final ClassInfo innerClassInfo = scanResult.getClassInfo(Super.class.getName()); assertThat(innerClassInfo).isNotNull(); @@ -107,18 +107,18 @@ public void testNonExtensionToInnerClass() { * the exception */ @Test - public void issue345b() throws Exception { + public void issue345b() { // Find URL of this class' classpath element URL classpathURL; - try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345.class.getName()).scan()) { - classpathURL = scanResult.getClassInfo(Issue345.class.getName()).getClasspathElementURL(); + try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345Test.class.getName()).scan()) { + classpathURL = scanResult.getClassInfo(Issue345Test.class.getName()).getClasspathElementURL(); } // Use this to create an override URLClassLoader try (ScanResult scanResult = new ClassGraph().enableClassInfo() .overrideClassLoaders(new URLClassLoader(new URL[] { classpathURL })).ignoreParentClassLoaders() .scan()) { // Assert that this class is found in its own classloader - assertThat(scanResult.getClassInfo(Issue345.class.getName())).isNotNull(); + assertThat(scanResult.getClassInfo(Issue345Test.class.getName())).isNotNull(); // But that other classpath elements on the classpath are not found assertThat(scanResult.getClassInfo(Test.class.getName())).isNull(); } @@ -148,7 +148,7 @@ public static class C extends B { @Test public void issue345c() { try (ScanResult scanResult = new ClassGraph().enableClassInfo() - .acceptPackages(Issue345.class.getPackage().getName()).ignoreClassVisibility().scan()) { + .acceptPackages(Issue345Test.class.getPackage().getName()).ignoreClassVisibility().scan()) { final ClassInfo ciA = scanResult.getClassInfo(A.class.getName()); assertThat(ciA.getModifiersStr()).isEqualTo("private static"); final ClassInfo ciB = scanResult.getClassInfo(B.class.getName()); diff --git a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java b/src/test/java/io/github/classgraph/issues/issue348/Issue348Test.java similarity index 98% rename from src/test/java/io/github/classgraph/issues/issue348/Issue348.java rename to src/test/java/io/github/classgraph/issues/issue348/Issue348Test.java index 8f7dfd850..f0a89abb6 100644 --- a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java +++ b/src/test/java/io/github/classgraph/issues/issue348/Issue348Test.java @@ -16,7 +16,7 @@ /** * Issue345. */ -public class Issue348 { +public class Issue348Test { /** Test for wildcarded jars. */ @Test public void testWildcard() { diff --git a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java b/src/test/java/io/github/classgraph/issues/issue350/Issue350Test.java similarity index 95% rename from src/test/java/io/github/classgraph/issues/issue350/Issue350.java rename to src/test/java/io/github/classgraph/issues/issue350/Issue350Test.java index 73fef1549..6f7405cbc 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350Test.java @@ -13,7 +13,7 @@ /** * Unit test. */ -public class Issue350 { +public class Issue350Test { /** * The Interface SuperclassAnnotation. @@ -68,14 +68,14 @@ public static class PrivSub extends Priv { /** Test finding subclasses of classes with annotated methods or fields. */ @Test public void test() { - try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350Test.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo().scan()) { assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); } - try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350Test.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo() .ignoreFieldVisibility().ignoreMethodVisibility().scan()) { assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class).getNames()) diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352Test.java similarity index 96% rename from src/test/java/io/github/classgraph/issues/issue352/Issue352.java rename to src/test/java/io/github/classgraph/issues/issue352/Issue352Test.java index 0d2a10ce3..795b50de9 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352Test.java @@ -15,7 +15,7 @@ /** * Unit test. */ -public class Issue352 { +public class Issue352Test { /** * Test *. @@ -46,7 +46,7 @@ public void test() throws IOException { .enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).contains(pkgInfoPath); } - try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue352.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue352Test.class.getPackage().getName()) .enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).doesNotContain(pkgInfoPath); } diff --git a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java b/src/test/java/io/github/classgraph/issues/issue355/Issue355Test.java similarity index 95% rename from src/test/java/io/github/classgraph/issues/issue355/Issue355.java rename to src/test/java/io/github/classgraph/issues/issue355/Issue355Test.java index 1f8042da9..adb2df02d 100644 --- a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java +++ b/src/test/java/io/github/classgraph/issues/issue355/Issue355Test.java @@ -20,7 +20,7 @@ /** * Unit test. */ -public class Issue355 { +public class Issue355Test { /** * Annotation parameter class. @@ -67,7 +67,7 @@ public void y(final X[] x) { @Test public void test() throws IOException { try (ScanResult scanResult = new ClassGraph() - .acceptPackagesNonRecursive(Issue355.class.getPackage().getName()).enableClassInfo() + .acceptPackagesNonRecursive(Issue355Test.class.getPackage().getName()).enableClassInfo() .enableInterClassDependencies().scan()) { final ClassInfo y = scanResult.getClassInfo(Y.class.getName()); final ClassInfo x = scanResult.getClassInfo(X.class.getName()); diff --git a/src/test/java/io/github/classgraph/issues/issue694/Issue694.java b/src/test/java/io/github/classgraph/issues/issue694/Issue694Test.java similarity index 85% rename from src/test/java/io/github/classgraph/issues/issue694/Issue694.java rename to src/test/java/io/github/classgraph/issues/issue694/Issue694Test.java index b9c1fe80c..c6b966da6 100644 --- a/src/test/java/io/github/classgraph/issues/issue694/Issue694.java +++ b/src/test/java/io/github/classgraph/issues/issue694/Issue694Test.java @@ -14,7 +14,7 @@ import io.github.classgraph.MethodInfo; import io.github.classgraph.ScanResult; -public class Issue694 { +public class Issue694Test { static class TestClass { } @@ -24,7 +24,7 @@ public static > C test(final C collection) { @Test void methodWithParam() { - final ScanResult scan = new ClassGraph().acceptClasses(Issue694.class.getName()).enableAnnotationInfo() + final ScanResult scan = new ClassGraph().acceptClasses(Issue694Test.class.getName()).enableAnnotationInfo() .enableMethodInfo().scan(); final List foundMethods = new ArrayList<>(); @@ -37,8 +37,8 @@ void methodWithParam() { } } assertThat(foundMethodInfo).containsOnly( - "public static > C test(final C collection)"); + "public static > C test(final C collection)"); assertThat(foundMethods).containsOnly( - "public static java.util.Collection io.github.classgraph.issues.issue694.Issue694.test(java.util.Collection)"); + "public static java.util.Collection io.github.classgraph.issues.issue694.Issue694Test.test(java.util.Collection)"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue696/Issue696.java b/src/test/java/io/github/classgraph/issues/issue696/Issue696Test.java similarity index 91% rename from src/test/java/io/github/classgraph/issues/issue696/Issue696.java rename to src/test/java/io/github/classgraph/issues/issue696/Issue696Test.java index fc9a6504b..43525e894 100644 --- a/src/test/java/io/github/classgraph/issues/issue696/Issue696.java +++ b/src/test/java/io/github/classgraph/issues/issue696/Issue696Test.java @@ -13,9 +13,9 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.MethodParameterInfo; import io.github.classgraph.ScanResult; -import io.github.classgraph.issues.issue696.Issue696.BrokenAnnotation.Dynamic; +import io.github.classgraph.issues.issue696.Issue696Test.BrokenAnnotation.Dynamic; -public class Issue696 { +public class Issue696Test { @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public static @interface Foo { @@ -35,7 +35,7 @@ public Dynamic(@Foo final String param1, @Bar final String param2) { @Test void genericSuperclass() { - final ScanResult scanResult = new ClassGraph().acceptPackages(Issue696.class.getPackage().getName()) + final ScanResult scanResult = new ClassGraph().acceptPackages(Issue696Test.class.getPackage().getName()) .enableMethodInfo().enableAnnotationInfo().scan(); final ClassInfo dynamic = scanResult.getClassInfo(Dynamic.class.getName()); final MethodParameterInfo[] paramInfo = dynamic.getConstructorInfo().get(0).getParameterInfo(); diff --git a/src/test/java/io/github/classgraph/issues/issue706/Issue706.java b/src/test/java/io/github/classgraph/issues/issue706/Issue706Test.java similarity index 93% rename from src/test/java/io/github/classgraph/issues/issue706/Issue706.java rename to src/test/java/io/github/classgraph/issues/issue706/Issue706Test.java index b090323dc..0e91258d7 100644 --- a/src/test/java/io/github/classgraph/issues/issue706/Issue706.java +++ b/src/test/java/io/github/classgraph/issues/issue706/Issue706Test.java @@ -10,7 +10,7 @@ import io.github.classgraph.TypeArgument; import io.github.classgraph.TypeVariableSignature; -public class Issue706 { +public class Issue706Test { static public class GenericBase { } @@ -19,7 +19,7 @@ static public class GenericBypass extends GenericBase { @Test void genericSuperclass() { - final ScanResult scanResult = new ClassGraph().acceptPackages(Issue706.class.getPackage().getName()) + final ScanResult scanResult = new ClassGraph().acceptPackages(Issue706Test.class.getPackage().getName()) .enableClassInfo().scan(); final ClassInfo bypassCls = scanResult.getClassInfo(GenericBypass.class.getName()); final TypeArgument superclassArg = bypassCls.getTypeSignature().getSuperclassSignature() diff --git a/src/test/java/io/github/classgraph/issues/issue735/Issue735.java b/src/test/java/io/github/classgraph/issues/issue735/Issue735Test.java similarity index 92% rename from src/test/java/io/github/classgraph/issues/issue735/Issue735.java rename to src/test/java/io/github/classgraph/issues/issue735/Issue735Test.java index 75d860c3a..ed634aaa0 100644 --- a/src/test/java/io/github/classgraph/issues/issue735/Issue735.java +++ b/src/test/java/io/github/classgraph/issues/issue735/Issue735Test.java @@ -8,8 +8,8 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.ScanResult; -public class Issue735 { - static interface Base { +public class Issue735Test { + interface Base { T get(); } @@ -24,7 +24,7 @@ static abstract class Derived2 implements Base { @Test void genericSuperclass() { - try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue735.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue735Test.class.getPackage().getName()) .enableAllInfo().ignoreClassVisibility().ignoreMethodVisibility().scan()) { final ClassInfo ci1 = scanResult.getClassInfo(Derived1.class.getName()); assertThat(ci1.getMethodInfo().get(0).getTypeSignatureOrTypeDescriptor().getResultType().toString()) diff --git a/src/test/java/io/github/classgraph/issues/issue739/Issue739.java b/src/test/java/io/github/classgraph/issues/issue739/Issue739Test.java similarity index 97% rename from src/test/java/io/github/classgraph/issues/issue739/Issue739.java rename to src/test/java/io/github/classgraph/issues/issue739/Issue739Test.java index fa70d3402..760cc9548 100644 --- a/src/test/java/io/github/classgraph/issues/issue739/Issue739.java +++ b/src/test/java/io/github/classgraph/issues/issue739/Issue739Test.java @@ -11,7 +11,7 @@ import io.github.classgraph.Resource; import io.github.classgraph.ScanResult; -public class Issue739 { +public class Issue739Test { @Test void wildcardPathSupport() { final String relPath = "src/test/resources/"; diff --git a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java b/src/test/java/io/github/classgraph/issues/issue766/Issue766Test.java similarity index 90% rename from src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java rename to src/test/java/io/github/classgraph/issues/issue766/Issue766Test.java index bc1ef0d8b..30bc6df14 100644 --- a/src/test/java/io/github/classgraph/issues/issue766/ClassgraphIssue766.java +++ b/src/test/java/io/github/classgraph/issues/issue766/Issue766Test.java @@ -11,11 +11,11 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -public class ClassgraphIssue766 { +public class Issue766Test { @Test public void testURLs() { - final URL url = ClassgraphIssue766.class.getResource("/issue766/ProjectWithAnnotations.iar"); + final URL url = Issue766Test.class.getResource("/issue766/ProjectWithAnnotations.iar"); final String fileUrl = "file:" + url.getPath(); final String jarFileUrl = "jar:file:" + url.getPath(); diff --git a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java b/src/test/java/io/github/classgraph/issues/issue93/Issue93Test.java similarity index 95% rename from src/test/java/io/github/classgraph/issues/issue93/Issue93.java rename to src/test/java/io/github/classgraph/issues/issue93/Issue93Test.java index 3d1f40151..cf03090f8 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93Test.java @@ -13,9 +13,9 @@ /** * Issue93. */ -public class Issue93 { +public class Issue93Test { /** The Constant PKG. */ - private static final String PKG = Issue93.class.getPackage().getName(); + private static final String PKG = Issue93Test.class.getPackage().getName(); /** * The Interface RetentionClass. diff --git a/src/test/resources/class-path-manifest-entry.jar b/src/test/resources/class-path-manifest-entry.jar index b18aa7a4777b920765e69a79cfe188c4b42823ae..0fc1d548d412b81c4d2052e67a8cb8283891ffa3 100644 GIT binary patch literal 631 zcmWIWW@Zs#U|`^2(A^arx&4cg+etozODtIbI(RCVCDL5s_SG#IXtK4Sj5Mtx-VwLM+-msukzN+$jvTb?d!%j={ zP1@6}KB4w+M3?u*JJViIePipjxIC!zf#!KJ36C!io;zmA^jde!vn&?e!7nt$sciG- zGZ#y{kN$G{qUrEVBlx!zlS<hKcoA3 zRbRfe{5dC}dn2Ey+MN8jIJIxT*L<$7vU}fjWBGg+fxXEMmh!AT2@8)h=4w=I&99zh zIX}pF*2=49p8f7clb_e0sXl*b4u8D6**3TSHT%a2 zlX@E&mr8``WR)yw{>;8|ZcgcoWwACAzyGq>K0eL8QS9mU%^rnafA(LP@%a(Q;i>Go zp7(Aw9D8AX?q1#gf5%QA<2{^GYVqOUu4|3$8t=nSe+pXTQn{sejy1>a$$xEbSF|*g z7M89MTYtdy$kWJqe;EV38JXn4(TpwfRe;eC0t#Rfds5(n$}%!YFw}0(n?6tJHY1Fd cZk*gU7swCrW@Q5z!3czDK>9Z@c`+~m06>BZ+5i9m literal 625 zcmWIWW@Zs#;Nak3unn;FV?Y8T3@i-3t|5-Po_=onzK(vLZmz*0dcJP|PBAbny?QWj zj)4Hfh2LJk`D2P$^fjWE-kIgAr5dy}=9aVm>d0ya;pD{|JMoD#qIo!~l=9HFN)|B6E}N9)?c_C!^W1N7zf9)7{BtGu<1XFbwf1*c z{5<{G{`ai8lNmbYxAR-euhDyPv6 zr?a1HmAROCyOx}^m~-vtJER@Y zX#Kn#hvYB1^}BZN`gi8=F;n04Qi~6tw_bAm{&nM@Noy~4t9V~pKIf-H!`lfK@snEw z0*+2^xB12FX1hH`M|?j+fHynGB94^v=NTCoqS+aMd`2b_21K$zmIEaVRDdgy1bDNu PfmAXAp&O7EWdiX4B7p?c From e1ca34d6efb75279b9c0df675a5dfe85536951ae Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 8 Nov 2024 12:18:07 +0100 Subject: [PATCH 684/713] [QuarkusClassLoaderHandler] Check for both `elements` and split element fields to accomdate Quarkus 3.15.x and earlier and 3.16.x and later. Fixes #391. --- .../QuarkusClassLoaderHandler.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index 931c55434..0b059bbf8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.io.IOError; import java.net.URI; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.Map; @@ -117,8 +118,17 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class @SuppressWarnings("unchecked") private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - for (final Object element : (Collection) classpathOrder.reflectionUtils.getFieldVal(false, - classLoader, "elements")) { + + Collection elements = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, + classLoader, "elements"); + if(elements == null) { + elements = new ArrayList<>(); + for(String fieldName : new String[] {"normalPriorityElements", "lesserPriorityElements"}) { + elements.addAll((Collection) classpathOrder.reflectionUtils.getFieldVal(false, + classLoader, fieldName)); + } + } + for (final Object element : elements) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, "file"), From 8e5cc652dae7a3493215f8f3e0d255b73fb64056 Mon Sep 17 00:00:00 2001 From: "Michael J. Simons" Date: Fri, 8 Nov 2024 21:53:04 +0100 Subject: [PATCH 685/713] Add a null check, also extract the logic into a separate method. --- .../QuarkusClassLoaderHandler.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index 0b059bbf8..a09b72c78 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -115,19 +115,10 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } } - @SuppressWarnings("unchecked") private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - Collection elements = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, - classLoader, "elements"); - if(elements == null) { - elements = new ArrayList<>(); - for(String fieldName : new String[] {"normalPriorityElements", "lesserPriorityElements"}) { - elements.addAll((Collection) classpathOrder.reflectionUtils.getFieldVal(false, - classLoader, fieldName)); - } - } + Collection elements = findQuarkusClassLoaderElements(classLoader, classpathOrder); for (final Object element : elements) { final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { @@ -145,6 +136,25 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl } } + @SuppressWarnings("unchecked") + private static Collection findQuarkusClassLoaderElements(ClassLoader classLoader, ClasspathOrder classpathOrder) { + Collection elements = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, + classLoader, "elements"); + if (elements == null) { + elements = new ArrayList<>(); + // Since 3.16.x + for (String fieldName : new String[] {"normalPriorityElements", "lesserPriorityElements"}) { + Collection fieldVal = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, + classLoader, fieldName); + if (fieldVal == null) { + continue; + } + elements.addAll(fieldVal); + } + } + return elements; + } + @SuppressWarnings("unchecked") private static void findClasspathOrderForRuntimeClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { From 515a24cf05d7a05d92ba66b213a81cbc6e525f8c Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 11 Nov 2024 16:20:17 +0100 Subject: [PATCH 686/713] Polish `QuarkusClassLoaderHandler` and `FastPathResolver`. This is a follow up on issue #891 and the preceding PR #893 and brings two changes: * The `QuarkusClassLoaderHandler` is a bit more explicit now about how the elements returned from the class loader are treated * The `FastPathResolver` can be simplified using only one pattern --- .../QuarkusClassLoaderHandler.java | 22 +++++++++++----- .../classgraph/utils/FastPathResolver.java | 26 +++++-------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index a09b72c78..b7bfcf62c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -33,6 +33,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; @@ -53,6 +55,15 @@ class QuarkusClassLoaderHandler implements ClassLoaderHandler { // Classloader since Quarkus 1.13 private static final String RUNNER_CLASSLOADER = "io.quarkus.bootstrap.runner.RunnerClassLoader"; + // Class path elements prior to Quarkus 3.11 + private static final Map PRE_311_RESOURCE_BASED_ELEMENTS; + static { + Map hlp = new HashMap<>(); + hlp.put("io.quarkus.bootstrap.classloading.JarClassPathElement", "file"); + hlp.put("io.quarkus.bootstrap.classloading.DirectoryClassPathElement", "root"); + PRE_311_RESOURCE_BASED_ELEMENTS = Collections.unmodifiableMap(hlp); + } + /** * Class cannot be constructed. */ @@ -119,13 +130,12 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { Collection elements = findQuarkusClassLoaderElements(classLoader, classpathOrder); + for (final Object element : elements) { final String elementClassName = element.getClass().getName(); - if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, "file"), - classLoader, scanSpec, log); - } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, "root"), + final String fieldName = PRE_311_RESOURCE_BASED_ELEMENTS.get(elementClassName); + if (fieldName != null) { + classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, fieldName), classLoader, scanSpec, log); } else { final Object rootPath = classpathOrder.reflectionUtils.invokeMethod(false, element, "getRoot"); @@ -137,7 +147,7 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl } @SuppressWarnings("unchecked") - private static Collection findQuarkusClassLoaderElements(ClassLoader classLoader, ClasspathOrder classpathOrder) { + private static Collection findQuarkusClassLoaderElements(final ClassLoader classLoader, final ClasspathOrder classpathOrder) { Collection elements = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, classLoader, "elements"); if (elements == null) { diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java index 9f3193b95..6bed8a4f6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -43,11 +43,8 @@ public final class FastPathResolver { /** Match %-encoded characters in URLs. */ private static final Pattern percentMatcher = Pattern.compile("([%][0-9a-fA-F][0-9a-fA-F])+"); - /** Match custom URLs that are followed by two slashes. */ - private static final Pattern schemeTwoSlashMatcher = Pattern.compile("^[a-zA-Z+\\-.]+://"); - - /** Match custom URLs that are followed by one slash. */ - private static final Pattern schemeOneSlashMatcher = Pattern.compile("^[a-zA-Z+\\-.]+:/"); + /** Match custom URLs that are followed by one or two slashes. */ + private static final Pattern schemeOneOrTwoSlashMatcher = Pattern.compile("^[a-zA-Z+\\-.]+:/{1,2}"); /** * Constructor. @@ -236,24 +233,15 @@ public static String resolve(final String resolveBasePath, final String relative } else { // Preserve the number of slashes on custom URL schemes (#420) final String relPath = startIdx == 0 ? relativePath : relativePath.substring(startIdx); - final Matcher m2 = schemeTwoSlashMatcher.matcher(relPath); - if (m2.find()) { + final Matcher matcher = schemeOneOrTwoSlashMatcher.matcher(relPath); + if (matcher.find()) { matchedPrefix = true; - final String m2Match = m2.group(); - startIdx += m2Match.length(); - prefix += m2Match; + final String match = matcher.group(); + startIdx += match.length(); + prefix += match; // Treat the part after the protocol as an absolute path, so the rest of the URL is not treated // as a directory relative to the current directory. isAbsolutePath = true; - } else { - final Matcher m1 = schemeOneSlashMatcher.matcher(relPath); - if (m1.find()) { - matchedPrefix = true; - final String m1Match = m1.group(); - startIdx += m1Match.length(); - prefix += m1Match; - isAbsolutePath = true; - } } } } while (matchedPrefix); From 37e0dc39e8ad7e55b314116eb24eb7f487a110b8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Nov 2024 23:25:47 -0700 Subject: [PATCH 687/713] [maven-release-plugin] prepare release classgraph-4.8.178 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ccba36f81..7ca6b03f0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.178-SNAPSHOT + 4.8.178 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.177 + classgraph-4.8.178 From 9ef4eb67fbecb0283a12f2663cc182566438c583 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Nov 2024 23:25:50 -0700 Subject: [PATCH 688/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7ca6b03f0..c3cf492d0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.178 + 4.8.179-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.178 + classgraph-4.8.177 From 31ee39fe17f893a54630fef24babedb30795d76a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Nov 2024 23:33:28 -0700 Subject: [PATCH 689/713] [maven-release-plugin] prepare release classgraph-4.8.179 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c3cf492d0..3cd0dda0f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.179-SNAPSHOT + 4.8.179 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.177 + classgraph-4.8.179 From 6f9012f2a193ebfefe4a4384e7642820e7aab0f5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 11 Nov 2024 23:34:00 -0700 Subject: [PATCH 690/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c29aa969..ec3258271 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.179 + 4.8.180-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 64b61a2fe03effcd5a6b8c5a6426d14f3681a1e2 Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Thu, 5 Dec 2024 16:45:03 -0500 Subject: [PATCH 691/713] Add a test to demonstrate https://github.com/classgraph/classgraph/issues/897. --- .../issues/issue897/Issue897Test.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue897/Issue897Test.java diff --git a/src/test/java/io/github/classgraph/issues/issue897/Issue897Test.java b/src/test/java/io/github/classgraph/issues/issue897/Issue897Test.java new file mode 100644 index 000000000..ef0a60a93 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue897/Issue897Test.java @@ -0,0 +1,57 @@ +package io.github.classgraph.issues.issue897; + +import static java.lang.annotation.ElementType.TYPE_USE; +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Target; +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.Resource; +import io.github.classgraph.ScanResult; + +/** + * Issue897. + */ +public class Issue897Test { + /** + * Inner class that uses another inner class. + */ + private class Inner1 { + public Inner1(@Anno Inner2 i) {} + } + + /** + * Other inner class. + */ + public class Inner2 { + } + + /** + * Type-use anntotation. + */ + @Target(TYPE_USE) + @interface Anno {} + + /** + * Test that the annotation is attached to the first "source-code" parameter of the Inner1 + * constructor, not to the compiler-generated parameter for the enclosing class. + */ + @Test + public void annotationOnInnerClassConstructor() { + try (ScanResult scanResult = new ClassGraph().acceptClasses(Inner1.class.getName()) + .ignoreClassVisibility().enableMethodInfo().enableAnnotationInfo().scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(Inner1.class.getName()); + final MethodInfo methodInfo = classInfo.getDeclaredConstructorInfo().get(0); + // TODO: Attach the annotation to the source-code parameter instead of crashing. + Assertions.assertThrows(IllegalArgumentException.class, + () -> methodInfo.getTypeSignatureOrTypeDescriptor()); + } + } +} From 16511fbd09e98688ea7f2216abad2b7568eb8527 Mon Sep 17 00:00:00 2001 From: kkrzywanski Date: Fri, 24 Jan 2025 12:02:01 +0100 Subject: [PATCH 692/713] Replace "String::replaceAll" with pre compiled pattern --- .../java/nonapi/io/github/classgraph/utils/JarUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java index 43bd70a77..10b7dc382 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java @@ -64,6 +64,9 @@ public final class JarUtils { /** The Constant TRAILING_DOTS. */ private static final Pattern TRAILING_DOTS = Pattern.compile("\\.$"); + /** The Constant DOUBLE_BACKSHLASH_WITH_COLON. */ + private static final Pattern DOUBLE_BACKSHLASH_WITH_COLON = Pattern.compile("\\\\:"); + /** * On everything but Windows, where the path separator is ':', need to treat the colon in these substrings as * non-separators, when at the beginning of the string or following a ':'. @@ -193,7 +196,7 @@ public static String[] smartPathSplit(final String pathStr, final char separator final int idx1 = splitPointsSorted.get(i); // Trim, and unescape "\\:" String part = pathStr.substring(idx0 + 1, idx1).trim(); - part = part.replaceAll("\\\\:", ":"); + part = DOUBLE_BACKSHLASH_WITH_COLON.matcher(part).replaceAll( ":"); // Remove empty path components if (!part.isEmpty()) { parts.add(part); From d579bcd699cdcf2ec00b06cd339624421a49ad97 Mon Sep 17 00:00:00 2001 From: sebastiantoh Date: Wed, 21 May 2025 11:59:30 -0400 Subject: [PATCH 693/713] Update comment to specify that bootstrap class loader is used instead of context classloader --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 274b8383b..7067119e5 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -89,8 +89,7 @@ public class ClassGraphClassLoader extends ClassLoader { // Only try environment classloaders if classpath and/or classloaders are not overridden if (!classpathOverridden && !classloadersOverridden) { - // Try the null classloader first (this will default to the context classloader of the class - // that called ClassGraph) + // Try the null classloader first (this will default to the bootstrap class loader) environmentClassLoaderDelegationOrder = new LinkedHashSet<>(); environmentClassLoaderDelegationOrder.add(null); From dbf57c74fa693078e5e3ad9d39f54f122d82de9d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Jun 2025 13:32:21 -0600 Subject: [PATCH 694/713] Don't show deprecation warning for Unsafe on JDK 24+ (#899) --- .../java/io/github/classgraph/ClassGraph.java | 5 +++ .../classgraph/fileslice/FileSlice.java | 2 ++ .../io/github/classgraph/utils/FileUtils.java | 35 ++++++------------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 4a7f09cce..3c3b9baab 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1423,10 +1423,15 @@ public ClassGraph setMaxBufferedJarRAMSize(final int maxBufferedJarRAMSize) { /** * If true, use a {@link MappedByteBuffer} rather than the {@link FileChannel} API to open files, which may be * faster for large classpaths consisting of many large jarfiles, but uses up virtual memory space. + * Not available on Java 24+ currently, because of the deprecation of the Unsafe API. * * @return this (for method chaining). */ public ClassGraph enableMemoryMapping() { + if (VersionFinder.JAVA_MAJOR_VERSION > 23) { + // See FileUtils.java + throw new IllegalArgumentException("enableMemoryMapping() is not supported on Java 24+"); + } scanSpec.enableMemoryMapping = true; return this; } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java index 548144d5a..d230cfc23 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -137,6 +137,8 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i this.isTopLevelFileSlice = true; if (nestedJarHandler.scanSpec.enableMemoryMapping) { + // TODO: for JDK 24+, use the new Arena API to memory-map the file to a MemorySegment: + // https://docs.oracle.com/en/java/javase/22/docs//api/java.base/java/nio/channels/FileChannel.html#map(java.nio.channels.FileChannel.MapMode,long,long,java.lang.foreign.Arena) try { // Try mapping file (some operating systems throw OutOfMemoryError if file // can't be mapped, some throw IOException) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 26953ec1d..395ac3ca7 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -537,30 +537,12 @@ private static void lookupCleanMethodPrivileged() { } catch (final ReflectiveOperationException | LinkageError e) { // Ignore } - } else { - //boolean jdkSuccess = false; - // // TODO: This feature is in incubation now -- enable after it leaves incubation. - // // To enable this feature, need to: - // // -- add whatever the "jdk.incubator.foreign" module name is replaced with to - // // in pom.xml, as an optional dependency - // // -- add the same module name to module-info.java as a "requires static" optional dependency - // // -- build two versions of module.java: the existing one, for --release=9, and a new version, - // // for --release=15 (or whatever the final release version ends up being when the feature is - // // moved out of incubation). - // try { - // // JDK 14+ Invoke MemorySegment.ofByteBuffer(myByteBuffer).close() - // // https://stackoverflow.com/a/26777380/3950982 - // memorySegmentClass = Class.forName("jdk.incubator.foreign.MemorySegment"); - // memorySegmentCloseMethod = AutoCloseable.class.getDeclaredMethod("close"); - // memorySegmentOfByteBufferMethod = memorySegmentClass.getMethod("ofByteBuffer", - // ByteBuffer.class); - // jdk14Success = true; - // } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e1) { - // // Fall through - // } - //if (!jdk14Success) { // In JDK9+, calling sun.misc.Cleaner.clean() gives a reflection warning on stderr, - // so we need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) instead, which makes - // the same call, but does not print the reflection warning. + } else if (VersionFinder.JAVA_MAJOR_VERSION < 24) { + // JDK 24+ reports: "A terminally deprecated method in sun.misc.Unsafe has been called" + // if Unsafe::invokeCleaner is used, and we don't actually need the cleaner method unless + // direct memory mapping is used rather than FileChannel (ClassGraph#enableMemoryMapping + // disables this now for JDK 24+). + // See: https://github.com/classgraph/classgraph/issues/899 try { Class unsafeClass; try { @@ -663,7 +645,7 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff // } // memorySegmentCloseMethod.invoke(memorySegment); // return true; - } else { + } else if (VersionFinder.JAVA_MAJOR_VERSION < 24) { if (theUnsafe == null) { if (log != null) { log.log("Could not unmap ByteBuffer, theUnsafe == null"); @@ -683,6 +665,9 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff // Buffer is a duplicate or slice return false; } + } else { + // TODO: on JDK 24+, use Arena -- see FileSlice + return false; } } catch (final ReflectiveOperationException | SecurityException e) { if (log != null) { From c8095ddd08c50ca2c73fb5bc8cb30404f584ca16 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Jun 2025 13:34:44 -0600 Subject: [PATCH 695/713] [maven-release-plugin] prepare release classgraph-4.8.180 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ec3258271..f64b5bbed 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.180-SNAPSHOT + 4.8.180 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.179 + classgraph-4.8.180 From 981366d117b3e72d414512a75432d3c787bd02fc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Jun 2025 13:34:47 -0600 Subject: [PATCH 696/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f64b5bbed..4efb39632 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.180 + 4.8.181-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 2e1d3353beeadb2c000595f809998f288fcaf2c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 30 Jun 2025 14:00:00 -0600 Subject: [PATCH 697/713] Add back JBoss code (#869) --- .../JBossClassLoaderHandler.java | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 4a7f6c6ba..752437e33 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.io.File; import java.lang.reflect.Array; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; @@ -108,38 +109,43 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas // PathResourceLoader has root field, which is a Path object final Object root = classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "root"); - String path = loadJarPathFromClassicVFS(root, classpathOrderOut); - if (!isPathExisting(path)) { - path = loadJarPathFromNewVFS(root, classpathOrderOut); - } - - if (path == null) { - final File file = (File) classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, - "fileOfJar"); - if (file != null) { - path = file.getAbsolutePath(); - } - } - if (path != null) { - classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); - } else { - if (log != null) { - log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); - } + boolean found = addIfExists(loadJarPathFromClassicVFS(root, classpathOrderOut), classLoader, + classpathOrderOut, scanSpec, log); + found |= addIfExists(loadJarPathFromNewVFS(root, classpathOrderOut), classLoader, classpathOrderOut, + scanSpec, log); + found |= addIfExists(classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "fileOfJar"), + classLoader, classpathOrderOut, scanSpec, log); + if (!found && log != null) { + log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); } } /** - * Checks if the given path exists and is a regular file. + * Checks if the given path exists and is a regular file, and if it does, adds it to the classpath. * - * @param path + * @param pathStr * the path to check - * @return true if the path exists and is a regular file, false otherwise + * @param classLoader + * the classloader + * @param classpathOrderOut + * the classpath order + * @param scanSpec + * the scan spec + * @param log + * the log */ - private static boolean isPathExisting(final String path) { - if (path != null && !path.isEmpty()) { - final Path possibleExistingPath = Paths.get(path); - return Files.exists(possibleExistingPath) && Files.isRegularFile(possibleExistingPath); + private static boolean addIfExists(final Object pathObj, final ClassLoader classLoader, + final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { + try { + Path path = pathObj == null ? null + : pathObj instanceof String && !((String) pathObj).isEmpty() ? Paths.get((String) pathObj) + : pathObj instanceof File ? ((File) pathObj).toPath() : null; + if (path != null && Files.exists(path) && Files.isRegularFile(path)) { + classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); + return true; + } + } catch (InvalidPathException e) { + // } return false; } @@ -158,7 +164,6 @@ private static boolean isPathExisting(final String path) { * @return The absolute path of the JAR file, or null if the path couldn't be found. */ private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut) { - if (root == null) { return null; } @@ -276,7 +281,6 @@ private static String loadJarPathFromClassicVFS(final Object root, final Classpa } } } - return path; } From 5a1a4c10057124cae89b35a5f28cdf1d300be017 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 30 Jun 2025 14:01:07 -0600 Subject: [PATCH 698/713] [maven-release-plugin] prepare release classgraph-4.8.181 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4efb39632..ec10b3084 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.181-SNAPSHOT + 4.8.181 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.180 + classgraph-4.8.181 From 8aee8c95e67f2b21024b03599ba18a1032d39884 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 30 Jun 2025 14:01:10 -0600 Subject: [PATCH 699/713] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ec10b3084..57483fff7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.181 + 4.8.182-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 7075641f135d3f7057acf9a47ca034109dcb9fb2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 30 Jun 2025 14:21:58 -0600 Subject: [PATCH 700/713] Try fixing JBossClassLoaderHandler (#896) --- .../JBossClassLoaderHandler.java | 85 +++++-------------- 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index 752437e33..fd0b28e21 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -30,10 +30,7 @@ import java.io.File; import java.lang.reflect.Array; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -109,45 +106,13 @@ private static void handleResourceLoader(final Object resourceLoader, final Clas // PathResourceLoader has root field, which is a Path object final Object root = classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "root"); - boolean found = addIfExists(loadJarPathFromClassicVFS(root, classpathOrderOut), classLoader, - classpathOrderOut, scanSpec, log); - found |= addIfExists(loadJarPathFromNewVFS(root, classpathOrderOut), classLoader, classpathOrderOut, + classpathOrderOut.addClasspathEntry(loadJarPathFromClassicVFS(root, classpathOrderOut), classLoader, + scanSpec, log); + classpathOrderOut.addClasspathEntry(loadJarPathFromNewVFS(root, classpathOrderOut), classLoader, scanSpec, + log); + classpathOrderOut.addClasspathEntry( + classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "fileOfJar"), classLoader, scanSpec, log); - found |= addIfExists(classpathOrderOut.reflectionUtils.getFieldVal(false, resourceLoader, "fileOfJar"), - classLoader, classpathOrderOut, scanSpec, log); - if (!found && log != null) { - log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); - } - } - - /** - * Checks if the given path exists and is a regular file, and if it does, adds it to the classpath. - * - * @param pathStr - * the path to check - * @param classLoader - * the classloader - * @param classpathOrderOut - * the classpath order - * @param scanSpec - * the scan spec - * @param log - * the log - */ - private static boolean addIfExists(final Object pathObj, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { - try { - Path path = pathObj == null ? null - : pathObj instanceof String && !((String) pathObj).isEmpty() ? Paths.get((String) pathObj) - : pathObj instanceof File ? ((File) pathObj).toPath() : null; - if (path != null && Files.exists(path) && Files.isRegularFile(path)) { - classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); - return true; - } - } catch (InvalidPathException e) { - // - } - return false; } /** @@ -161,40 +126,35 @@ private static boolean addIfExists(final Object pathObj, final ClassLoader class * The root object to get the JAR path from. * @param classpathOrderOut * The ClasspathOrder object for updating the classpath order. - * @return The absolute path of the JAR file, or null if the path couldn't be found. + * @return The {@link File} of the JAR file, or null if the path couldn't be found. */ - private static String loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut) { + private static File loadJarPathFromNewVFS(final Object root, final ClasspathOrder classpathOrderOut) { if (root == null) { return null; } - final Class jbossVFS = getJBossVFSAccess(root); if (jbossVFS == null) { return null; } - // try to find the mount of the root. Type is org.jboss.vfs.VFS.Mount final Object mount = classpathOrderOut.reflectionUtils.invokeStaticMethod(false, jbossVFS, "getMount", root.getClass(), root); if (mount == null) { return null; } - // try to access the fileSystem of the mount. Type is org.jboss.vfs.spi.FileSystem final Object fileSystem = classpathOrderOut.reflectionUtils.invokeMethod(false, mount, "getFileSystem"); if (fileSystem == null) { return null; } - // now access the mount source, which is the file that is used to create the mount. final File mountSource = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, fileSystem, "getMountSource"); if (mountSource == null) { return null; } - // absolute path of the mountSource should be the 'physical' .jar - return mountSource.getAbsolutePath(); + return mountSource; } /** @@ -249,10 +209,12 @@ private static Class loadJBossVFS(final ClassLoader classLoader) throws Class * The root object to get the JAR path from. * @param classpathOrderOut * The ClasspathOrder object for updating the classpath order. - * @return The absolute path of the JAR file, or null if the path couldn't be found. + * @return The {@link File} or {@link Path} of the JAR file, or null if the VFS path couldn't be found. */ - private static String loadJarPathFromClassicVFS(final Object root, final ClasspathOrder classpathOrderOut) { - String path = null; + private static Object loadJarPathFromClassicVFS(final Object root, final ClasspathOrder classpathOrderOut) { + if (root == null) { + return null; + } // type VirtualFile final File physicalFile = (File) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPhysicalFile"); @@ -262,26 +224,21 @@ private static String loadJarPathFromClassicVFS(final Object root, final Classpa // getParentFile() removes "contents" directory final File file = new File(physicalFile.getParentFile(), name); if (FileUtils.canRead(file)) { - path = file.getAbsolutePath(); + return file; } else { // This is an exploded jar or classpath directory - path = physicalFile.getAbsolutePath(); + return physicalFile; } } else { - path = physicalFile.getAbsolutePath(); + return physicalFile; } } else { - path = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPathName"); - if (path == null) { - // Try Path or File - final File file = root instanceof Path ? ((Path) root).toFile() - : root instanceof File ? (File) root : null; - if (file != null) { - path = file.getAbsolutePath(); - } + String path = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPathName"); + if (path != null) { + return path; } + return root; } - return path; } /** From 42943e445c8ddb3ea30d9ecef011ec8cb68f377f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 30 Jun 2025 14:23:28 -0600 Subject: [PATCH 701/713] Bump version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 57483fff7..953e5c066 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.182-SNAPSHOT + 4.8.181-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6310533c138b2fd5013e0146c8de8ff9c70ce152 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Jul 2025 16:02:08 -0600 Subject: [PATCH 702/713] Use all applicable ClassLoaderHandlers (#906) --- pom.xml | 68 ++-------------- .../AntClassLoaderHandler.java | 4 +- .../ClassGraphClassLoaderHandler.java | 4 +- .../CxfContainerClassLoaderHandler.java | 5 +- .../EquinoxClassLoaderHandler.java | 4 +- ...quinoxContextFinderClassLoaderHandler.java | 4 +- .../FelixClassLoaderHandler.java | 9 ++- .../JBossClassLoaderHandler.java | 6 +- .../JPMSClassLoaderHandler.java | 7 +- .../OSGiDefaultClassLoaderHandler.java | 4 +- ...DelegationOrderTestClassLoaderHandler.java | 4 +- ...assWorldsClassRealmClassLoaderHandler.java | 4 +- .../QuarkusClassLoaderHandler.java | 27 ++++--- .../SpringBootRestartClassLoaderHandler.java | 5 +- .../TomcatWebappClassLoaderBaseHandler.java | 4 +- .../URLClassLoaderHandler.java | 3 +- .../UnoOneJarClassLoaderHandler.java | 7 +- .../WeblogicClassLoaderHandler.java | 16 ++-- .../WebsphereLibertyClassLoaderHandler.java | 6 +- ...ebsphereTraditionalClassLoaderHandler.java | 10 ++- .../classpath/ClassLoaderFinder.java | 22 ++++++ .../classpath/ClassLoaderOrder.java | 77 +++++++------------ .../classgraph/classpath/ClasspathFinder.java | 42 +++++----- 23 files changed, 166 insertions(+), 176 deletions(-) diff --git a/pom.xml b/pom.xml index 953e5c066..95159881b 100644 --- a/pom.xml +++ b/pom.xml @@ -38,17 +38,6 @@ https://github.com/classgraph/classgraph/issues - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - UTF-8 @@ -148,7 +137,6 @@ - @@ -217,16 +205,6 @@ maven-gpg-plugin 3.2.3 - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - - - org.apache.maven.plugins - maven-release-plugin - 3.0.1 - @@ -234,21 +212,19 @@ maven-clean-plugin 3.3.2 - - org.apache.maven.plugins - maven-deploy-plugin - 3.1.1 - org.apache.maven.plugins maven-install-plugin 3.1.1 - - org.apache.maven.plugins - maven-site-plugin - 3.12.1 - + + + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + @@ -544,34 +520,6 @@ - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - ossrh - https://oss.sonatype.org/ - true - 10 - - - - - - org.apache.maven.plugins - maven-release-plugin - - true - - release - - -Prelease - - diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java index 6489ecbec..3a3b66a04 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,7 +50,8 @@ private AntClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.tools.ant.AntClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.apache.tools.ant.AntClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java index ca3a054f5..256157df6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.net.URL; import io.github.classgraph.ClassGraphClassLoader; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -55,7 +56,8 @@ private ClassGraphClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - final boolean matches = "io.github.classgraph.ClassGraphClassLoader".equals(classLoaderClass.getName()); + final boolean matches = ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "io.github.classgraph.ClassGraphClassLoader"); if (matches && log != null) { log.log("Sharing a `ClassGraphClassLoader` between multiple nested scans is not advisable, " + "because scan criteria may differ between scans. " diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java index 62af46256..dbd6be4d4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/CxfContainerClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,8 +50,8 @@ private CxfContainerClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.openejb.server.cxf.transport.util.CxfContainerClassLoader" - .equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.apache.openejb.server.cxf.transport.util.CxfContainerClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java index 9e387f29e..ab601a60b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -32,6 +32,7 @@ import java.util.HashSet; import java.util.Set; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -64,7 +65,8 @@ private EquinoxClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.eclipse.osgi.internal.loader.EquinoxClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.eclipse.osgi.internal.loader.EquinoxClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java index a797ce7a7..cc0311455 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,7 +50,8 @@ private EquinoxContextFinderClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.eclipse.osgi.internal.framework.ContextFinder".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.eclipse.osgi.internal.framework.ContextFinder"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java index 0c9193f20..de442aa10 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Set; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.reflection.ReflectionUtils; @@ -62,10 +63,10 @@ private FelixClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5" - .equals(classLoaderClass.getName()) - || "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader" - .equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java index fd0b28e21..99f46aa80 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -38,6 +38,7 @@ import java.util.Map.Entry; import java.util.Set; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -65,7 +66,8 @@ private JBossClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.jboss.modules.ModuleClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.jboss.modules.ModuleClassLoader"); } /** @@ -233,7 +235,7 @@ private static Object loadJarPathFromClassicVFS(final Object root, final Classpa return physicalFile; } } else { - String path = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPathName"); + final String path = (String) classpathOrderOut.reflectionUtils.invokeMethod(false, root, "getPathName"); if (path != null) { return path; } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java index 9c6ed4590..b214ad999 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -30,6 +30,7 @@ import java.net.URL; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -54,8 +55,10 @@ private JPMSClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "jdk.internal.loader.ClassLoaders$AppClassLoader".equals(classLoaderClass.getName()) - || "jdk.internal.loader.BuiltinClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "jdk.internal.loader.ClassLoaders$AppClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "jdk.internal.loader.BuiltinClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java index 93074c222..517bc0622 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -30,6 +30,7 @@ import java.io.File; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -55,7 +56,8 @@ private OSGiDefaultClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java index 0cae55c23..532585748 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,7 +50,8 @@ private ParentLastDelegationOrderTestClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "io.github.classgraph.issues.issue267.FakeRestartClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "io.github.classgraph.issues.issue267.FakeRestartClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java index 9c68301b1..e48ed2a77 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -30,6 +30,7 @@ import java.util.SortedSet; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.reflection.ReflectionUtils; @@ -56,7 +57,8 @@ private PlexusClassWorldsClassRealmClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.codehaus.plexus.classworlds.realm.ClassRealm".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.codehaus.plexus.classworlds.realm.ClassRealm"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java index b7bfcf62c..9476d2115 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.Map; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -58,7 +59,7 @@ class QuarkusClassLoaderHandler implements ClassLoaderHandler { // Class path elements prior to Quarkus 3.11 private static final Map PRE_311_RESOURCE_BASED_ELEMENTS; static { - Map hlp = new HashMap<>(); + final Map hlp = new HashMap<>(); hlp.put("io.quarkus.bootstrap.classloading.JarClassPathElement", "file"); hlp.put("io.quarkus.bootstrap.classloading.DirectoryClassPathElement", "root"); PRE_311_RESOURCE_BASED_ELEMENTS = Collections.unmodifiableMap(hlp); @@ -80,9 +81,9 @@ private QuarkusClassLoaderHandler() { * @return true, if classLoaderClass is the Quarkus RuntimeClassloader or QuarkusClassloader */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return RUNTIME_CLASSLOADER.equals(classLoaderClass.getName()) - || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName()) - || RUNNER_CLASSLOADER.equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, RUNTIME_CLASSLOADER) + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, QUARKUS_CLASSLOADER) + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, RUNNER_CLASSLOADER); } /** @@ -129,14 +130,15 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - Collection elements = findQuarkusClassLoaderElements(classLoader, classpathOrder); + final Collection elements = findQuarkusClassLoaderElements(classLoader, classpathOrder); for (final Object element : elements) { final String elementClassName = element.getClass().getName(); final String fieldName = PRE_311_RESOURCE_BASED_ELEMENTS.get(elementClassName); if (fieldName != null) { - classpathOrder.addClasspathEntry(classpathOrder.reflectionUtils.getFieldVal(false, element, fieldName), - classLoader, scanSpec, log); + classpathOrder.addClasspathEntry( + classpathOrder.reflectionUtils.getFieldVal(false, element, fieldName), classLoader, + scanSpec, log); } else { final Object rootPath = classpathOrder.reflectionUtils.invokeMethod(false, element, "getRoot"); if (rootPath instanceof Path) { @@ -147,15 +149,16 @@ private static void findClasspathOrderForQuarkusClassloader(final ClassLoader cl } @SuppressWarnings("unchecked") - private static Collection findQuarkusClassLoaderElements(final ClassLoader classLoader, final ClasspathOrder classpathOrder) { + private static Collection findQuarkusClassLoaderElements(final ClassLoader classLoader, + final ClasspathOrder classpathOrder) { Collection elements = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, - classLoader, "elements"); + classLoader, "elements"); if (elements == null) { elements = new ArrayList<>(); // Since 3.16.x - for (String fieldName : new String[] {"normalPriorityElements", "lesserPriorityElements"}) { - Collection fieldVal = (Collection) classpathOrder.reflectionUtils.getFieldVal(false, - classLoader, fieldName); + for (final String fieldName : new String[] { "normalPriorityElements", "lesserPriorityElements" }) { + final Collection fieldVal = (Collection) classpathOrder.reflectionUtils + .getFieldVal(false, classLoader, fieldName); if (fieldVal == null) { continue; } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java index 9687ff2b2..73187a8fa 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -55,8 +56,8 @@ private SpringBootRestartClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.springframework.boot.devtools.restart.classloader.RestartClassLoader" - .equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.springframework.boot.devtools.restart.classloader.RestartClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java index e4604291a..aa5a9f2c1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -31,6 +31,7 @@ import java.io.File; import java.util.List; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.reflection.ReflectionUtils; @@ -53,7 +54,8 @@ private TomcatWebappClassLoaderBaseHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "org.apache.catalina.loader.WebappClassLoaderBase"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java index bf7399876..9b2e9c4d9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.net.URL; import java.net.URLClassLoader; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -52,7 +53,7 @@ private URLClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "java.net.URLClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, "java.net.URLClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java index 7077270a9..570705e0b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,8 +50,10 @@ private UnoOneJarClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "com.needhamsoftware.unojar.JarClassLoader".equals(classLoaderClass.getName()) - || "com.simontuffs.onejar.JarClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "com.needhamsoftware.unojar.JarClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "com.simontuffs.onejar.JarClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java index 5d00ef5a4..dbd257951 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -49,13 +50,18 @@ private WeblogicClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "weblogic.utils.classloaders.ChangeAwareClassLoader".equals(classLoaderClass.getName()) - || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoaderClass.getName()) - || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoaderClass.getName()) + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "weblogic.utils.classloaders.ChangeAwareClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "weblogic.utils.classloaders.GenericClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "weblogic.utils.classloaders.FilteringClassLoader") // TODO: The following two known classloader names have not been tested, and the fields/methods // may not match those of the above classloaders. - || "weblogic.servlet.jsp.JspClassLoader".equals(classLoaderClass.getName()) - || "weblogic.servlet.jsp.TagFileClassLoader".equals(classLoaderClass.getName()); + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "weblogic.servlet.jsp.JspClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "weblogic.servlet.jsp.TagFileClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java index 261ca0509..d47958beb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.reflection.ReflectionUtils; @@ -75,8 +76,9 @@ private WebsphereLibertyClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return IBM_APP_CLASS_LOADER.equals(classLoaderClass.getName()) - || IBM_THREAD_CONTEXT_CLASS_LOADER.equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, IBM_APP_CLASS_LOADER) + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + IBM_THREAD_CONTEXT_CLASS_LOADER); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java index ea0e3c011..05d6918eb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import nonapi.io.github.classgraph.classpath.ClassLoaderFinder; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -53,9 +54,12 @@ private WebsphereTraditionalClassLoaderHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "com.ibm.ws.classloader.CompoundClassLoader".equals(classLoaderClass.getName()) - || "com.ibm.ws.classloader.ProtectionClassLoader".equals(classLoaderClass.getName()) - || "com.ibm.ws.bootstrap.ExtClassLoader".equals(classLoaderClass.getName()); + return ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "com.ibm.ws.classloader.CompoundClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "com.ibm.ws.classloader.ProtectionClassLoader") + || ClassLoaderFinder.classIsOrExtendsOrImplements(classLoaderClass, + "com.ibm.ws.bootstrap.ExtClassLoader"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java index 99159fa8b..95a958a06 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java @@ -52,6 +52,28 @@ public ClassLoader[] getContextClassLoaders() { // ------------------------------------------------------------------------------------------------------------- + /** Return true if the class is, extends, or implements a given named class or interface. */ + // TODO: make this a default method of the ClassLoaderHandler interface in ClassGraph 5.x + public static boolean classIsOrExtendsOrImplements(Class cls, String className) { + if (cls == null) { + return false; + } + if (cls.getName().equals(className)) { + return true; + } + if (classIsOrExtendsOrImplements(cls.getSuperclass(), className)) { + return true; + } + for (Class iface : cls.getInterfaces()) { + if (classIsOrExtendsOrImplements(iface, className)) { + return true; + } + } + return false; + } + + // ------------------------------------------------------------------------------------------------------------- + /** * A class to find the unique ordered classpath elements. * diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java index f9699a1b7..2394178a5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -28,17 +28,16 @@ */ package nonapi.io.github.classgraph.classpath; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import io.github.classgraph.ClassGraph; -import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry.ClassLoaderHandlerRegistryEntry; import nonapi.io.github.classgraph.reflection.ReflectionUtils; @@ -47,7 +46,7 @@ /** A class to find all unique classloaders. */ public class ClassLoaderOrder { /** The {@link ClassLoader} order. */ - private final List> classLoaderOrder = new ArrayList<>(); + private final Map> classLoaderOrder = new LinkedHashMap<>(); public ReflectionUtils reflectionUtils; @@ -74,10 +73,6 @@ public class ClassLoaderOrder { private final Set allParentClassLoaders = Collections .newSetFromMap(new IdentityHashMap()); - /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ - private final Map classLoaderToClassLoaderHandlerRegistryEntry = // - new IdentityHashMap<>(); - // ------------------------------------------------------------------------------------------------------------- public ClassLoaderOrder(final ReflectionUtils reflectionUtils) { @@ -90,8 +85,8 @@ public ClassLoaderOrder(final ReflectionUtils reflectionUtils) { * @return the {@link ClassLoader} order, as a pair: {@link ClassLoader}, * {@link ClassLoaderHandlerRegistryEntry}. */ - public List> getClassLoaderOrder() { - return classLoaderOrder; + public List>> getClassLoaderOrder() { + return new ArrayList<>(classLoaderOrder.entrySet()); } /** @@ -103,42 +98,22 @@ public Set getAllParentClassLoaders() { return allParentClassLoaders; } - /** - * Find the {@link ClassLoaderHandler} that can handle a given {@link ClassLoader} instance. - * - * @param classLoader - * the {@link ClassLoader}. - * @param log - * the log - * @return the {@link ClassLoaderHandlerRegistryEntry} for the {@link ClassLoader}. - */ - private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader classLoader, final LogNode log) { - ClassLoaderHandlerRegistryEntry entry = classLoaderToClassLoaderHandlerRegistryEntry.get(classLoader); - if (entry == null) { - // Try all superclasses of classloader in turn - for (Class currClassLoaderClass = classLoader.getClass(); // - currClassLoaderClass != Object.class && currClassLoaderClass != null; // - currClassLoaderClass = currClassLoaderClass.getSuperclass()) { - // Find a ClassLoaderHandler that can handle the ClassLoader - for (final ClassLoaderHandlerRegistryEntry ent : ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS) { - if (ent.canHandle(currClassLoaderClass, log)) { - // This ClassLoaderHandler can handle the ClassLoader class, or one of its superclasses - entry = ent; - break; - } - } - if (entry != null) { - // Don't iterate to next superclass if a matching ClassLoaderHandler was found - break; - } - } - if (entry == null) { - // Use fallback handler - entry = ClassLoaderHandlerRegistry.FALLBACK_HANDLER; + /** Get the ClassLoaderHandler(s) that can handle a given ClassLoader. */ + private static List getClassLoaderHandlerRegistryEntries( + final ClassLoader classLoader, final LogNode log) { + List ents = new ArrayList<>(); + boolean matched = false; + for (final ClassLoaderHandlerRegistryEntry ent : ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS) { + if (ent.canHandle(classLoader.getClass(), log)) { + // This ClassLoaderHandler can handle the ClassLoader class, or one of its superclasses + ents.add(ent); + matched = true; } - classLoaderToClassLoaderHandlerRegistryEntry.put(classLoader, entry); } - return entry; + if (!matched) { + ents.add(ClassLoaderHandlerRegistry.FALLBACK_HANDLER); + } + return ents; } /** @@ -154,10 +129,7 @@ public void add(final ClassLoader classLoader, final LogNode log) { return; } if (added.add(classLoader)) { - final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); - if (entry != null) { - classLoaderOrder.add(new SimpleEntry<>(classLoader, entry)); - } + classLoaderOrder.put(classLoader, getClassLoaderHandlerRegistryEntries(classLoader, log)); } } @@ -183,10 +155,13 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent, fi } // Don't delegate to a classloader twice if (delegatedTo.add(classLoader)) { - // Find ClassLoaderHandlerRegistryEntry for this classloader - final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); - // Delegate to this classloader, by recursing to that classloader to get its classloader order - entry.findClassLoaderOrder(classLoader, this, log); + add(classLoader, log); + // Recurse to get delegation order + // (note: may be wrong if multiple ClassLoaderHandlers can handle this classloader) + for (final ClassLoaderHandlerRegistryEntry entry : getClassLoaderHandlerRegistryEntries(classLoader, + /* Don't log twice -- also logged by add method above */ null)) { + entry.findClassLoaderOrder(classLoader, this, log); + } } } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index be1e2bcc4..09e3581bb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -262,28 +262,30 @@ public ClasspathFinder(final ScanSpec scanSpec, final ReflectionUtils reflection final LogNode classloaderURLLog = classpathFinderLog == null ? null : classpathFinderLog.log("Obtaining URLs from classloaders in delegation order"); final List finalClassLoaderOrder = new ArrayList<>(); - for (final Entry ent : classLoaderOrder + for (final Entry> ent : classLoaderOrder .getClassLoaderOrder()) { final ClassLoader classLoader = ent.getKey(); - final ClassLoaderHandlerRegistryEntry classLoaderHandlerRegistryEntry = ent.getValue(); - // Add classpath entries to ignoredClasspathOrder or classpathOrder - if (!scanSpec.ignoreParentClassLoaders || !allParentClassLoaders.contains(classLoader)) { - // Otherwise add classpath entries to classpathOrder, and add the classloader to the - // final classloader ordering - final LogNode classloaderHandlerLog = classloaderURLLog == null ? null - : classloaderURLLog - .log("Classloader " + classLoader.getClass().getName() + " is handled by " - + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); - classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, - classloaderHandlerLog); - finalClassLoaderOrder.add(classLoader); - } else if (classloaderURLLog != null) { - classloaderURLLog.log("Ignoring parent classloader " + classLoader + ", normally handled by " - + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); - } - // See if a previous scan's ClassGraphClassLoader should be delegated to first - if (classLoader instanceof ClassGraphClassLoader) { - delegateClassGraphClassLoader = (ClassGraphClassLoader) classLoader; + for (final ClassLoaderHandlerRegistryEntry classLoaderHandlerRegistryEntry : ent.getValue()) { + // Add classpath entries to ignoredClasspathOrder or classpathOrder + if (!scanSpec.ignoreParentClassLoaders || !allParentClassLoaders.contains(classLoader)) { + // Otherwise add classpath entries to classpathOrder, and add the classloader to the + // final classloader ordering + final LogNode classloaderHandlerLog = classloaderURLLog == null ? null + : classloaderURLLog.log("Classloader " + classLoader.getClass().getName() + + " is handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); + classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, + classloaderHandlerLog); + finalClassLoaderOrder.add(classLoader); + } else if (classloaderURLLog != null) { + classloaderURLLog + .log("Ignoring parent classloader " + classLoader + ", normally handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); + } + // See if a previous scan's ClassGraphClassLoader should be delegated to first + if (classLoader instanceof ClassGraphClassLoader) { + delegateClassGraphClassLoader = (ClassGraphClassLoader) classLoader; + } } } From da15ece153a138944f35af7d5242852dbe9fb8e8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Jul 2025 02:03:20 -0600 Subject: [PATCH 703/713] v4.8.181 --- pom.xml | 1154 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 609 insertions(+), 545 deletions(-) diff --git a/pom.xml b/pom.xml index 95159881b..b8331e3bc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,572 +1,636 @@ - - 4.0.0 + + 4.0.0 - io.github.classgraph - classgraph - 4.8.181-SNAPSHOT - ClassGraph + io.github.classgraph + classgraph + 4.8.181 + ClassGraph - The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. + The uber-fast, ultra-lightweight classpath and module scanner + for JVM languages. - https://github.com/classgraph/classgraph + https://github.com/classgraph/classgraph - - - The MIT License (MIT) - http://opensource.org/licenses/MIT - repo - - + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + - - - Luke Hutchison - luke.hutch@gmail.com - ClassGraph - https://github.com/classgraph - - + + + Luke Hutchison + luke.hutch@gmail.com + ClassGraph + https://github.com/classgraph + + - - scm:git:git@github.com:classgraph/classgraph.git - scm:git:git@github.com:classgraph/classgraph.git - https://github.com/classgraph/classgraph - classgraph-4.8.181 - + + scm:git:git@github.com:classgraph/classgraph.git + scm:git:git@github.com:classgraph/classgraph.git + https://github.com/classgraph/classgraph + classgraph-4.8.181 + - - https://github.com/classgraph/classgraph/issues - + + https://github.com/classgraph/classgraph/issues + - - - UTF-8 - UTF-8 + + + UTF-8 + UTF-8 - - + + - - - - - + + + + + - - - - - io.github.toolfactory - narcissus - 1.0.7 - true - - - io.github.toolfactory - jvm-driver - 9.7.1 - true - + + + + + io.github.toolfactory + narcissus + 1.0.7 + true + + + io.github.toolfactory + jvm-driver + 9.7.1 + true + - - - org.junit.jupiter - junit-jupiter - 5.10.2 - test - - - org.openjdk.jmh - jmh-core - 1.37 - test - - - org.openjdk.jmh - jmh-generator-annprocess - 1.37 - test - - - org.assertj - assertj-core - 3.25.3 - test - - - javax.enterprise - cdi-api - 2.0 - test - - - org.ops4j.pax.url - pax-url-aether - 2.6.14 - test - - - org.slf4j - slf4j-api - 2.0.13 - test - - - org.slf4j - slf4j-jdk14 - 2.0.13 - test - - - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - 1.0.2.Final - test - - - com.google.jimfs - jimfs - 1.3.0 - test - - - jakarta.validation - jakarta.validation-api - 3.0.2 - test - + + + org.junit.jupiter + junit-jupiter + 5.10.2 + test + + + org.openjdk.jmh + jmh-core + 1.37 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.37 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + javax.enterprise + cdi-api + 2.0 + test + + + org.ops4j.pax.url + pax-url-aether + 2.6.14 + test + + + org.slf4j + slf4j-api + 2.0.13 + test + + + org.slf4j + slf4j-jdk14 + 2.0.13 + test + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.2.Final + test + + + com.google.jimfs + jimfs + 1.3.0 + test + + + jakarta.validation + jakarta.validation-api + 3.0.2 + test + - - - - - - - org.eclipse.jdt - org.eclipse.jdt.annotation - 2.3.0 - provided - - + + + + + + + org.eclipse.jdt + org.eclipse.jdt.annotation + 2.3.0 + provided + + - - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.4.1 - - - org.apache.maven.plugins - maven-resources-plugin - 3.3.1 - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - - org.codehaus.mojo - build-helper-maven-plugin - 3.5.0 - - - org.apache.maven.plugins - maven-jar-plugin - 3.4.0 - - - org.apache.maven.plugins - maven-antrun-plugin - 3.1.0 - - - org.apache.maven.plugins - maven-source-plugin - 3.3.1 - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.3 - - - org.apache.maven.plugins - maven-gpg-plugin - 3.2.3 - + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.3 + - - - org.apache.maven.plugins - maven-clean-plugin - 3.3.2 - - - org.apache.maven.plugins - maven-install-plugin - 3.1.1 - - - - - org.sonatype.central - central-publishing-maven-plugin - 0.8.0 - true - - + + + org.apache.maven.plugins + maven-clean-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-install-plugin + 3.1.1 + - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - org.codehaus.mojo - animal-sniffer-enforcer-rule - 1.23 - - - - - - enforce-versions - validate - - enforce - - - - - [3.6.3,) - - - - - - - check-signatures - compile - - enforce - - - - - - org.codehaus.mojo.signature - java17 - 1.0 - - - - - - - + + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + central + true + + + - - org.apache.maven.plugins - maven-resources-plugin - - - - copy-license-to-target - process-resources - - copy-resources - - - ${project.build.outputDirectory} - - - ${basedir} - false - - LICENSE-ClassGraph.txt - - - - - - - copy-license-to-javadocs - process-resources - - copy-resources - - - ${project.build.directory}/apidocs - - - ${basedir} - false - - LICENSE-ClassGraph.txt - - - - - - - + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.codehaus.mojo + animal-sniffer-enforcer-rule + 1.23 + + + + + + enforce-versions + validate + + enforce + + + + + [3.6.3,) + + + + + + + check-signatures + compile + + enforce + + + + + + org.codehaus.mojo.signature + java17 + 1.0 + + + + + + + - - org.apache.maven.plugins - maven-antrun-plugin - - - - - - - - - - - add-module-info-to-jar - package - - run - - - - - - - - - - - - - - - add-modular-javadoc - verify - - run - - - - - - - - - - - + + org.apache.maven.plugins + maven-resources-plugin + + + + copy-license-to-target + process-resources + + copy-resources + + + + ${project.build.outputDirectory} + + + ${basedir} + false + + LICENSE-ClassGraph.txt + + + + + + + copy-license-to-javadocs + process-resources + + copy-resources + + + + ${project.build.directory}/apidocs + + + ${basedir} + false + + LICENSE-ClassGraph.txt + + + + + + + - - - org.apache.maven.plugins - maven-compiler-plugin - - UTF-8 - - - - - - - - - - - - - 7 - 7 - - 8 - 8 - false - - -Xlint:all - -Xlint:-options - -Werror - - - - - default-testCompile - test-compile - - testCompile - - - UTF-8 - - 8 - 8 - - -parameters - - - - - + + org.apache.maven.plugins + maven-antrun-plugin + + + + + + + + + + + add-module-info-to-jar + package + + run + + + + + + + + + + + + + + + add-modular-javadoc + verify + + run + + + + + + + + + + + - - - org.apache.maven.plugins - maven-surefire-plugin - + + + org.apache.maven.plugins + maven-compiler-plugin + + UTF-8 + + + + + + + + + + + + + 7 + 7 + + 8 + 8 + false + + -Xlint:all + -Xlint:-options + -Werror + + + + + default-testCompile + test-compile + + testCompile + + + UTF-8 + + 8 + 8 + + -parameters + + + + + - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-test-source - generate-test-sources - - add-test-source - - - - src/test/perf - - - - - + + + org.apache.maven.plugins + maven-surefire-plugin + - - - org.apache.maven.plugins - maven-jar-plugin - - true - - true - - true - true - - - - Utilities - http://opensource.org/licenses/MIT - 2 - ClassGraph - ${project.groupId}.${project.artifactId} - Luke Hutchison - ${project.description} - ${project.version} - osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" - io.github.classgraph;version="${project.version}" - - - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" - - java.xml,jdk.unsupported,java.management,java.logging - - - true - - - - + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/perf + + + + + - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - package - - jar-no-fork - - - - + + + org.apache.maven.plugins + maven-jar-plugin + + true + + true + + true + true + + + + Utilities + + http://opensource.org/licenses/MIT + 2 + ClassGraph + + ${project.groupId}.${project.artifactId} + Luke Hutchison + ${project.description} + ${project.version} + + osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" + + io.github.classgraph;version="${project.version}" + + + + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + + + java.xml,jdk.unsupported,java.management,java.logging + + + true + + + + - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - package - - jar - - - 8 - ${javadoc.html.version} - all - nonapi.* - public - - - - - - - + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + package + + jar-no-fork + + + + - - - - jdk9plus - - -html5 - - - true - - - [9,) - - + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + package + + jar + + + 8 + ${javadoc.html.version} + all + nonapi.* + public + + + + + + + - - - release - - - - org.apache.maven.plugins - maven-gpg-plugin - - - --pinentry-mode - loopback - - ${gpg.keyname} - ${gpg.keyname} - - - - sign-artifacts - verify - - sign - - - - - - - - + + + + jdk9plus + + -html5 + + + true + + + [9,) + + + + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + + + --pinentry-mode + loopback + + ${gpg.keyname} + ${gpg.keyname} + + + + sign-artifacts + verify + + sign + + + + + + + + From 00d23b9cec24b31a6c90e072648dece33b74d902 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Wed, 16 Jul 2025 17:38:01 +0200 Subject: [PATCH 704/713] Revert formatting changes of pom.xml from da15ece153a138944f35af7d5242852dbe9fb8e8 Cancels what seems to be 80-char hard wraps, which caused some strings to no longer be the same. Fixes #909 --- pom.xml | 170 ++++++++++++++++++-------------------------------------- 1 file changed, 55 insertions(+), 115 deletions(-) diff --git a/pom.xml b/pom.xml index b8331e3bc..2aaf93000 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,4 @@ - + 4.0.0 io.github.classgraph @@ -8,8 +6,7 @@ 4.8.181 ClassGraph - The uber-fast, ultra-lightweight classpath and module scanner - for JVM languages. + The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. https://github.com/classgraph/classgraph @@ -49,17 +46,13 @@ - - + + - + - + @@ -143,15 +136,11 @@ test - + - - - + + + org.eclipse.jdt org.eclipse.jdt.annotation @@ -162,13 +151,10 @@ - + - - + + org.apache.maven.plugins maven-enforcer-plugin @@ -220,8 +206,7 @@ 3.2.3 - + org.apache.maven.plugins maven-clean-plugin @@ -284,8 +269,7 @@ - + org.codehaus.mojo.signature java17 @@ -310,8 +294,7 @@ copy-resources - - ${project.build.outputDirectory} + ${project.build.outputDirectory} ${basedir} @@ -330,8 +313,7 @@ copy-resources - - ${project.build.directory}/apidocs + ${project.build.directory}/apidocs ${basedir} @@ -351,21 +333,13 @@ maven-antrun-plugin - - - - - - - + + + + + + + add-module-info-to-jar package @@ -374,24 +348,17 @@ - - + + - - - - + + + + add-modular-javadoc verify @@ -399,12 +366,8 @@ - - + + @@ -418,26 +381,17 @@ maven-compiler-plugin UTF-8 - - - - - + + + + + - - - + + + - + 7 7 @@ -447,8 +401,7 @@ false -Xlint:all - -Xlint:-options + -Xlint:-options -Werror @@ -513,33 +466,22 @@ Utilities - - http://opensource.org/licenses/MIT + http://opensource.org/licenses/MIT 2 ClassGraph - - ${project.groupId}.${project.artifactId} + ${project.groupId}.${project.artifactId} Luke Hutchison ${project.description} ${project.version} - - osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" - - io.github.classgraph;version="${project.version}" - - - - javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" - - - java.xml,jdk.unsupported,java.management,java.logging - - + osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" + io.github.classgraph;version="${project.version}" + + + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional",io.github.toolfactory.narcissus;resolution:="optional",io.github.toolfactory.jvm;resolution:="optional" + + java.xml,jdk.unsupported,java.management,java.logging + + true @@ -593,8 +535,7 @@ -html5 - + true @@ -602,8 +543,7 @@ - + release From 4d941924a724c6696707ce392d86cec9285eee0e Mon Sep 17 00:00:00 2001 From: Aurimas Niekis Date: Tue, 23 Sep 2025 15:05:03 +0900 Subject: [PATCH 705/713] feat(scan): propagate ScanResult to module and package annotations Introduce setScanResult methods in ModuleInfo and PackageInfo to assign ScanResult context to their associated annotations. This enables annotations to access scanning metadata when class info is enabled. Called from ScanResult during post-processing. --- src/main/java/io/github/classgraph/ModuleInfo.java | 8 ++++++++ src/main/java/io/github/classgraph/PackageInfo.java | 8 ++++++++ src/main/java/io/github/classgraph/ScanResult.java | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 57b5801e8..dfbc5ae22 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -217,6 +217,14 @@ public PackageInfoList getPackageInfo() { // ------------------------------------------------------------------------------------------------------------- + void setScanResult(final ScanResult scanResult) { + if (annotationInfoSet != null) { + for (final AnnotationInfo ai : annotationInfoSet) { + ai.setScanResult(scanResult); + } + } + } + /** * Add annotations found in a module descriptor classfile. * diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 6d10566c6..f617ed7e6 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -124,6 +124,14 @@ void addClassInfo(final ClassInfo classInfo) { // ------------------------------------------------------------------------------------------------------------- + void setScanResult(final ScanResult scanResult) { + if (annotationInfoSet != null) { + for (final AnnotationInfo ai : annotationInfoSet) { + ai.setScanResult(scanResult); + } + } + } + /** * Get a the annotation on this package, or null if the package does not have the annotation. * diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 52fe67e8b..83bbaec8e 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -352,6 +352,16 @@ private void indexResourcesAndClassInfo(final LogNode log) { ci.setReferencedClasses(new ClassInfoList(refdClassesFiltered, /* sortByName = */ true)); } } + + if (scanSpec.enableClassInfo) { + for (final PackageInfo pkgInfo : packageNameToPackageInfo.values()) { + pkgInfo.setScanResult(this); + } + + for (final ModuleInfo moduleInfo : moduleNameToModuleInfo.values()) { + moduleInfo.setScanResult(this); + } + } } // ------------------------------------------------------------------------------------------------------------- From fbe61e01e8fadd8cf98dd7891c80e5000c85d6c0 Mon Sep 17 00:00:00 2001 From: Christopher Hubert Date: Tue, 23 Sep 2025 15:38:35 +0100 Subject: [PATCH 706/713] [Issue 920] Fixing getMethodInfo to not get superconstructors --- .../java/io/github/classgraph/ClassInfo.java | 8 +++-- .../issues/issue920/Issue920Test.java | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue920/Issue920Test.java diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index c85ee2f20..afc44dfcf 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2332,11 +2332,13 @@ private MethodInfoList getMethodInfo(final String methodName, final boolean getN final MethodInfoList methodInfoList = new MethodInfoList(); final Set> nameAndTypeDescriptorSet = new HashSet<>(); for (final ClassInfo ci : getMethodOverrideOrder()) { - for (final MethodInfo mi : ci.getDeclaredMethodInfo(methodName, getNormalMethods, getConstructorMethods, + // Constructors are not inherited from superclasses + boolean shouldGetConstructorMethods = ci == this && getConstructorMethods; + for (final MethodInfo mi : ci.getDeclaredMethodInfo(methodName, getNormalMethods, shouldGetConstructorMethods, getStaticInitializerMethods)) { - // If method/constructor has not been overridden by method of same name and type descriptor + // If method has not been overridden by method of same name and type descriptor if (nameAndTypeDescriptorSet.add(new SimpleEntry<>(mi.getName(), mi.getTypeDescriptorStr()))) { - // Add method/constructor to output order + // Add method to output order methodInfoList.add(mi); } } diff --git a/src/test/java/io/github/classgraph/issues/issue920/Issue920Test.java b/src/test/java/io/github/classgraph/issues/issue920/Issue920Test.java new file mode 100644 index 000000000..209b73c2a --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue920/Issue920Test.java @@ -0,0 +1,35 @@ +package io.github.classgraph.issues.issue920; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.MethodInfoList; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Modifier; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * ClassGraph used to return incorrect modifiers for non-public constructors if + * there is a public constructor of same signature in the superclass AND `ignoreMethodVisibility` has not been set. + * In that case it will instead return the super's constructor's modifiers. + */ +public class Issue920Test { + @Test + void test() { + MethodInfoList constructors = new ClassGraph() + .enableAnnotationInfo() + .enableSystemJarsAndModules() + .enableClassInfo() + .enableMethodInfo() + .scan() + .getClassInfo("java.io.ObjectOutputStream") + .getConstructorInfo(); + for (MethodInfo constructor : constructors) { + if (constructor.getParameterInfo().length == 0) { + // The no args constructor of ObjectOutputStream is protected + assertEquals(Modifier.PROTECTED, constructor.getModifiers(), "The no-args constructor of ObjectOutputStream should read as `protected`"); + } + } + } +} From f1b1a4fcd49cb84cd8028928eb1a82a967cea372 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 01:45:02 -0600 Subject: [PATCH 707/713] Update version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2aaf93000..fa47288ae 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.181 + 4.8.182 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From f329d650a73b4e4738704ff5e2573aa40f50437e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 02:14:25 -0600 Subject: [PATCH 708/713] Remove EOL JVM-Driver support (Narcissus is still supported) --- .mvn/wrapper/MavenWrapperDownloader.java | 117 ----- .mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 4 +- mvnw | 475 +++++++++--------- mvnw.cmd | 371 +++++++------- pom.xml | 10 +- .../java/io/github/classgraph/ClassGraph.java | 17 +- .../reflection/JVMDriverReflectionDriver.java | 205 -------- .../reflection/ReflectionUtils.java | 7 - .../EncapsulationCircumventionTest.java | 17 +- 10 files changed, 432 insertions(+), 791 deletions(-) delete mode 100644 .mvn/wrapper/MavenWrapperDownloader.java delete mode 100644 .mvn/wrapper/maven-wrapper.jar delete mode 100644 src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index c32394f14..000000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed 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. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.5"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 0d5e649888a4843c1520054d9672f80c62ebbb48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50710 zcmbTd1F&Yzk}llaw%yydZQHhOtG8|2wr$%sdfWEC{mnUpfBrjP%(-twMXZRmGOM!c zd9yOJo|2OU0!ID;4i5g~#}E8J?LU7Ie;%cUmH4T}WkhI!e#l9J{q@Zcz<+)r_dg0E z|5rh2ei?BQVMQexX_2HDe#ihic;RQiO?))5*`S|S7OJR$0!15$@o}&gh{KEX8>-aS zebwz)UwGRGE9?4DhKZ)R2wjvy<%rYe_z!fyA~>e=tmvNPLiuHP53`)W`FLgV1o9b@ z?3)Q4hagTgvBzZDa`v_DRkmwm>bk&&5@m;ZKwovq%oDWOE5u zleR0Z)LP%g z*ydlFD2)HVxVbHjlfI?CgZaOti1hCi{oA;xT^;o8?2H}$CAG}|d$o49)--kwwtsqX zGBi1>nE^FB$)DBl&kl0=BkJj!u8pT3X-SM$t*%!O7Tx#?VUN(J@J7 z%mqmlxhp6bH9rj)^iYq`pf?`O*$x~aBDK%&CjpjW0Dmepb(vLDTzk@0d>tccth>%{ zqcr7aeZu!Zr23hdL)!RGizX}aWJj6ClX4Gb=bet4tBUy?-|r{nUh$7yJ*eiA?Z;B2`eF1LaPBSu_fx@B5isJF5&|yU7hLsa5}05d3gQRmO4{!66oMh zigvqS{W+|Y0wOi($g$qiEf^jL)}>W~AR*|m?Ia0Mm&;BjorRn-!}CxKVO!7^_eSU; za}~KI`cHaF*!+>B5a-KI>36u#or|tTiuzm;hLCR>bMq9@2Z1fr4d$A`%|rCLKl^5z z`Z~yYPy)~i?x3_LE7|;0GLF#mVOpQ8X>1gNNLX!4rWD(!q!EVsGZPum^~IQ?OAy9U z#lqI;WcC{U(KHra8q6HKa`%NZ^;gqs))9Mb3hgxa%QY1dO_YQok3%a5hFXmwyQwt5 zokv+V7DJgXNlo1Jv9u21JB$WF~oaC)aF8zY-VK6{ynvH6F zk|{{&#%crN>5Vm&6byp)q(XYXIF)9Q`;lMGWJIP3e)3zmi0gVmI|;n*$`v-Jtj5!h>;@Y&fY9%VqR zdvyz`W~hk%)WdNHVGkD6tdf`iv8B&HpjCgRcx=@$^CrBuzraY$k`dZ&LmR8t+(FSQ zL7=y~l+GL+%Xzvj66Xb`Ey}35$xDv5O2@5ywUr2_>Jz*srt`dPuFp2>5mTdt>H7NR zvg!zAScv9uGBZa^gCeh77YJ4_0xc@0!jSG}P@Pn!)t0|+UFI7!?W90^55Ha1de+3Y zNz}7<*xPlOFN5;J!=rS=Zwb(PT)j`|B_(F8EmsvkQZ1wGuG&Xu)OZmTR0Y99D$5#tf%OElqb{J^!W*E8vy2$QkhN-E(3>~vNdny^ z&_#^RRL>0Mog`;hZ~2=uUwy|8W@gdO$pq$;8M?Z?{ z(!g)#LR-;l-oCvHxx--!6D~z2_%z~DPIcWwnzgGa&;ouDP~Bx#u>)3HUKjSUTv2kS z*jfLRyc-Yu(ClrUvuAvfnmu_BkvFbTk8>#tYv@*?nq_h~A!A!yM;do9 zC^E#;pW}3;$ApFCRQo(dyU5c>3TcRmq%|Z|8p^lxDmk7JN6llr_&U?Rg|@NljYOR2 zb=vg=oS1GN>(^NCAaiE9rbhk__1Nwu!OuPddM7KQJj)Bezh85DvUl}a?!*ZJEMKfp zbU*8SY`{iQ=%fl0#Af$k6~2*0v^?llf1Emdn5Q5YG+%7`*5uyO_^txn^`x2l^J_As2-4_Tm|5b}0q$5okF$ zHaO03%@~_Z=jpV!WTbL$}e;NgXz=Uw!ogI}+S@aBP**2Wo^yN#ZG z4G$m^yaM9g?M5E1ft8jOLuzc3Psca*;7`;gnI0YzS0%f4{|VGEzKceaptfluwyY#7 z^=q#@gi@?cOm99Qz!EylA4G~7kbF7hlRIzcrb~{_2(x@@z`7d96Bi_**(vyr_~9Of z!n>Gqk|ZWyu!xhi9f53&PM3`3tNF}pHaq}(;KEn#pmm6DZBu8*{kyrTxk<;mx~(;; z1NMrp@Zd0ZqI!oTJo3b|HROE}UNcQash!p5eLjTcz)>kP=Bp@z)5rLGnaF5{~@z;MFCP9s_dDdADddy z{|Zd9ou-;laEHid_b7A^ zBw1J-^uo$K|@udwk;w* za_|mNqh!k}0fkzR#`|v?iVB@HJt^?0Fo^YGim=lqWD&K7$=J2L(HMp@*5YwV1U)1Aj@><#btD=m0Ga1X))fcKJ=s(v}E7fc1fa_$nGP%d9Opjh3) zRid3zuc5^mNmnnsg4G>m;Sfh@hH$ZT$p%QswzSRa2bh;(7lOaWT>Jv@Ki>_Ep?jx7 z&hwEG^YF=vEgvUwjT_VgWlSZeS{CTjedc)A>N0*uAU(9G@5|><%)^NxRcyx@4!m3s z%1?oiq^@>V!+tKZka-ax2e-`Deeb9_AaTF~z;arjq>Im$ zMc`JAOruhFrFTj6I-Al5$^z4tyu_l2Qk04>>;9#)B#fF})h0_OHP)%xv~m#T+6VG< zP6O@;?5g^t6wm{HX+54ZPoe%(;HU^*OPSEojLYRFRE~=mPXE!0pb|Zs=psR=-v`L# zB2`|mvJBoNTvW`LJ}a;cHP~jC@klxY0|ec3Y!w-`mQ6>CzF}GQCHmrB>k3`fk=3Ck z+WwgG3U_aN&(|RY$ss6CYZ(%4!~tuVWSHu?q=6{-Izay&o_Mvxm=!*?C-NQZFC8=n{?qfRf$3o_VSHs%zfSMdMQ5_f3xt6~+{RX=$H8at z9Si~lTmp}|lmm;++^zA%Iv+XJAHcTf1_jRxfEgz$XozU8$D?08YntWwMY-9iyk@u#wR?JxR2bky5j9 z3Sl-dQQU?#rO0xa)Sp<|MJnx@%w#GcXXM7*Vs=VPdSFt5$aJux89D%D?lA0_j&L42 zcyGz!opsIob%M&~(~&UkX0ndOq^MqjxXw8MIN}U@vAKq_fp@*Vp$uVFiNfahq2MzA zU`4uR8m$S~m+h{-pKVzp%Gs(Wz+%>h;R9Sg-MrB38r?e_Tx6PD%>)bi(#$!a@*_#j zCKr_wm;wtEtOCDwzW25?t{~PANe*e(EXogwcq&Ysl-nT2MBB3E96NP8`Ej_iQFT@X zG22M5ibzYHNJ~tR(et8lDFp|we$&U1tZ33H-o#?o$(o&(>aCNWlMw#Y{b}!fw$6_p z{k}778KP{PZ`c87HBXWDJK)sKXU5xF2))N*t_1C^~Q5(q1W#@r0y#QUke zY9@kew61E>;G2Ds$-gvm=pMuXW~T4Tv@ZhzZkH)DZ_mlk!&rL#E+5JaIx|cf&@b{g ziV)ouh%FU9i6D+C!e&>1x91bwV26SChDV1};|%rXHfqfEpP9?svl6*wM_)kY1DlTX zVN?D2ru8SysDeW~0<@G�zysyX$qy=e$fT3I);zi(d{LG!_|v^=p4+LvsaO4ZCN~ zB-KmIW}S_KN_ATX;5;x^db&s|}S8E#kzLatD!GN+|kuC<-^@23Y! z*;N4OIffqekU*ZaeTLtsHRzwQKbwq>RI6t0q&$~4;x_R!j1^WDlIWM;4owb|LaUU;gB#MA@JqI#y;!{{X|Dopjjm?}-C%NvfAIc8KU4twNO{gMnKTHPgD_kgT>dPikq_{#R~- z5_LG$FSLUqOdW;v1Sld5H;iO?Kt~1>?KtDuV~QlMHwU1aUdmH2gDOt#2doNPh*b#| zj*nPhH-OXD^b|$QA2mZwnAQ5#*o;#inRD_HLwn9_qvcj5qS$^Yzr%^V?>svB2OgQa zwb)=f5m@1E6{{~15H$w6r>|_>&!pWVf>~#bcLb7PI#F2VX+|c^cxRYg&Rf-g+-+8Y z+9b3@@uoR2Bq#b(GR}?7e?R`l7gp&^LqAg<39sS{n)*aB#u2+xXKf+_@NCse$b#x> z|D853NTEM!txFmuZ8~B&9*E?|7&T6{ePv{9!U&CK=H^@W*dbvN(+dW(86zl_2SRqP zVz1T$USo{^tp6su9fqL}hRYP2kXl7zv=9Bn*2NMrfQhT&#$P@F8ojHpeo#G{UN)Iu zdyFTF6Xog5MPav;ZC%%W)qUR&gnUzG9AFiT?H=GzZZ6FKLWIy$S~hi#wUT9KwV+!!3ux(uIY&xNOy#_ zb@YdgY}y@5sivI8BEhQ<)Xve#*}|P)>n+>UHSP72oB%los3Hnc@M*l^04)-w?h#El zLnO=xj4vs{#Y3SZyJTN7gLy-Z6bZHV{H-j>HQ)Dia)VL&*G8}J&5qXvX9;%%O%?6& zymuDI1Z2O%G2gl0tF2evSCQCMwY8zQjaDzY-8}2#$9nyGauUh5mPja>5XSRj}YzFxKs12=Ie0gr;4-rl7ES2utCIaTjqFNg{V`5}Rdt~xE^I;Bwp4)|cs8=f)1YwHz zp?r7}s2~qsDV+gL1e}}NpUE#`^Aq8l%yL9DyQeXSADg5*qMprGAELiHg0Q39`O+i1 z!J@iV!`Y~C$wJ!5?|2X&h?5r(@)tBG$JL=!*uk=2k;T<@{|s1xYL079FvK(6NMedO zP8^EEZnp`(hVMZ;sTk(k5YXnG-b6v;nlw+^* zEwj5-yyMEI3=z&TduBb3HLKz9{|qCfLrTof>=V;1r2y;LT3N)to9fNmN^_w;gpvtr z#4Z->#;&${rrl6`uidUzwT0ab5cAd(eq1^_;`7#H*J0NAJlc@Q>a;+uk$1Fo%q1>V ztuCG3YmenEJhn45P;?%`k@Y>ot+ZzKw9qU`LM| z5^tVL}`9?D;Hzd>_%ptW6 z#N#GToeLGh=K(xh3^-Wj zJpQ)7Zzj6MZdx3^Jn@dh#&_`!w5*<+z^_z~Zc1EyN73#a8yMu*us=j$zX|$sa7Qja zJqh|s-0NjR=L@{4^RexB5aiQJk-m~K^0-AnoCz)nOyncC9+EzeaOQ;W`3Fy|tX21Z zYS`m6!*in{AkaUR|EZKLvNDL+D#(Pz#TTPwImog9dM47L2Ha*RhaXuWuVNEk zv^yjmQQilZpE!xi)2UL9FThU@%XPr@><}RDNOnAZVo7F@UzrdfIeQ}ztxG;_5D8{x zpghA^U4P0{+lr65_?%+D?R-Z|%F4h9&{UhTF&^rKK@f1|DYh1V+z?V5Y7DoHO;E04 zspYSv9AuJII$U~Vbe9+yNypV&&?1%5*S@Sm!g@KaK*D-8e_jd`d3{_7GkL8lN20!~ zSPC<%ss zq}c{_ZD89J{JbXK-yZNh=_2;Spj0~&Rmdy@G~6|)6IWLW0jN_~ZwBq!r;7F}yhPMw zyGvM6nVXhJVb3P#P^wo6Z79Mus9+P-E zn<4+(Z00{oIR8jvgroal`}p94zw;8~W8Hp$q0z8RcM-&i5e2?mkT#ZWnJAyHVRQWo zLDUQsCt>vcvL*RGaPI(0&ArSQKsR%QXGrRc8xlXN6w)_JuSZbSE)|-Hje-i9jWVVY zCRpOHe4+=#$V2c!5b$mFdJku;)298132#glg?KN(>C4atl4%gDXow)md;WfQq-vT& zL$Y%hKKUSwlx&yzsU(lOCd9m0fz9X#b2@`^U(GKka``>d5|X z8pLfJo%F4&{{5gKOU+#m`?vEqw|S9z)o@CrRm1=l=xeOA9+pvT)Ga=S5RtlC^5D82 z<8t)jPzUD(Zn9DJFKa~bJ#g{9U^~uf0N{n%dIUWUKy$@)rc>c{CTsKbZR)P;)*e<* zGu3#c0Xz+F#+~==PoHb=`>mX=FVtTs4wHOgdT~g27WD?py|^9Z2A2&5(gXICs0|0w zmvch%kRg|?05N(`)XO{-CG42L%3p)78)BYwkMaX%@s{urW?yoQC%DBEl!tb z+qIV({K_N1-m(n1;jmQ*ldFehGiLQOkR?{M6fYE{)aVjKNPxDp7}3Evlw_rsYy}oo z>I9tCT81hPGr>ar(HF(_{zaxdE81dX1-~r?=j0r+a^H`!Dd1h2GgBTRxH2+xF9pfV zr6vcp_)q7Jy;0zmGH&t|RPUuzQ}I)m5W?5B%SLTDyQc_%oO2lUg5E3L#Bv&FxyQKi z+fU*dE#u%YtnXn4ttri0=4<>be51WT)4n68^vuXmTH^6Z+fCF-eDF)m9m%XHJDTGF zIEy_YfPDHk!(NVDJJpEjIN#gfT&=Cox92;W20|ojSNW{vzaAn<;#~#@5vh#9gD(nk zwn)`Foh-(wGTz2RI2N(gbSCGv80UV8_#sF%3LA{cuN-W^Xh~#g&6j3boo%h#=n-r4 zzTONgkxjx=zE4PLMVm0JmzcL3+r`_YJ>=-LptK4UcoP?JWwCqf%qGnj2CAm1g;bpW zc=Snp-L_MK9X)Fsj)3uZR`gGIHyh=uw6L<#l7A@g^IoduM7G|<3opaWkZR123QBQe z00cg!%35wF(b@x%^mL~rWQlDI`05vX#~75`3=_F9oA05`X!XIX77X!|g`nXw{BmX! z6m;1XDruiW3Ww$3vFdvSZ9h$jNopc#&JX!Lm^j}U6XH_xz^q7YD$fFP(xubauVuWz z<6GkJyg;wwwaAO^O5pP-(*t@MEMCWM2zY2v@Mg*Wfeu@(C>6lg2d_U zXkydADuMO6yx@Eu(!0C8t@4I)Kim_!gvMDPqnrH|Q0~ zM1vX0ItXknO){#fNgWNwScueS#7wP-InL$k5%`gmg2$Q*%%nHTm8!0ibosAkct7cz zUtu!`{C5zJG1se79|^BUxb762i~QxxNp5PlPY5KIx6w9S7W)w|h#0}~EQ%BQ&si;v zvBI8D+-qFH1E9DiHj1v&*nLQqpQYUKnb5pz2KW0D7wlDM?#|A1$j6!?Mde@a>w}D# zX4D@r9Y`{4NsY{4OGn32Ts7Slqe4+C6%?Y$S@x^2$%U7xXyIx_fkbJjdmDr zG3TY$_(^f=PBth@PU$(P>s!2$RLv%3)7@|mtg4-wo7s7oU+B4BNs3}s989xGNB*`oRQ~ocNDijOq26fjIl>+`e#NPDIsyiIXm) zO6rQjqHyQsl_p6IiTj+=@|BQ}zDkR^rcmMq&oQ33;P>sMy?7ccB1k+i zzGvMKP%A`m~)r;gNhP zBG|G-*d?Gi=i|R|0=eVu^)%Ie#t7U-pL(u|zVIUP4w%;;dE;Lt+v}s4I;$NZ#VH87 zNoFz{FCfRDmeE@U#b;!-s*Yo9;c||hjW4zHvdCZf5XeRBz|$^`yL%W~*v&?7^i?%K z2?~03DjYqn7t|@mQ*5XZHB_~y7Ei{eO{!~X^Yxl{>v@o^<^rHFWNgQ>Kitlni=V*J z8&xA_4J@Yp91m4yN^uuvZ(19gFDzGzqNrJLaXH%8Dl7#rdER!XgTXFZgt!JY4@OiE}3b32Pzbj)nI7kKeR7Br|x zFR(8p8qdMMMM8=K+g?R_3k5jVrgJ83ZYTPrPbmW`?T@mhzag=Dq36?8PJvqDhJ*7M z0{U4XGtN6%(UWf%&O~EnuHG79nFT(v<+PHK2@Y4^C{=zs*iZ~EVbHOrTvBXqb4KD- z&pMMu663ByI}OEAJj3+~A1el$m5AEkh>#bjKl}^vf=j&adgZY0GLlE$6Bc?oqF_v18Ix%3(Zw?{!V=p{lIxU6SIk<4$I{0U}@ znuoM`TGm!vNuyX}Ok@KCxC{MNwpj+F1w`;;HRctuLQtmg;0uBl2u`*zW@F6+S(osl zTvrKIpkiQV8PFO)4gh%NaFh9FGYSLK43{Ek@zGdr;Y=uSsWxHK1&J)Fjs9jG8yJXV zx=Ohi7D%i|h>hT{lPMvC;>|N1bOO&N-EtcUVLFeZGCG1F>}4r9qu`q}hp)qjt$2we zacGRO$2cn_%FV~IS~VW=F>6StmI}!`2guXSr=Jcb~qj;b#nxT)|t4%GlNo} zo-yQLi!cprmaZK3oadq|cp*}4sy$IjFo8HziwdsYPr%mFS+Azxn1UU=tO=7jXCoKb zip6_)Q>vdzvhRoZ?t`%*?gyzdo{HT+W8$amGE=a^wb~60Jv&??XvYkLKNRqRMWJB1 zX+q3@<+IG(P1d_`+lvL^C}4-90*LuRnRiC;-4{O-FPODpxiGBN#SQ9H2+B;JqhDnfLY&c`Hbsh*Nbd_6nZ zl9=4Ovg803&N()m4bzp_yjrrARDUr~a$e!;?Bd?vw8ZsDm-ZHMwfhtN@I6AG9&-QH zp+LW1tt1Dra(n>zr90}1%cETiD2XOVUyjdP+I|8|b7kQMcaAl$<^rr5T|iD3jp7%K zq{bY)q)csIS*0Z=qmr2^5Lb=N47!L*t@wXzq;4}I>+)>*)t}$y!`^)Wbs92AHPo@ zdua*H4TdfzFK?I&g5+RhbwlA4(mh_lf?~mq!q!Gx`Zs#^rRq2uu&9jhOc7_XlSpv& zndOJPFccid+ddXM_uV{N{~Jh&K@0jn#U;~#GqEHPLjA!642j_ zfmuhn!AA{O@pb#89k4lnb8lW8od-;6nP}7Kwt2wq=&Mxsa(!U>WVx^N15Z?r|MniI zEn#jJy1{bGdF@aQzRA!^!Y5|kYq{aR+M)4&vG&Tr@J@Ny1>1a7_?Eoo^it)I`UdSe zujc6wdEwSLC^&+;1@lr3gDVXbe@*MctM`z2$bj|zo~`QQb(pwUu5OH7i8&DUqyK14 zF!!3!uRQGGg=kFdS<+HjzhDo(w-~SBrtDBd_w_+fdW0dpT|j)mdk||XX}?%o;4RAu zof1gVjZI&#T;yLg0DoK!m}u1rsXedYXgOLrw)E_>1k>a`D0NA^S)|f<_P(23i(7lg zf0lS~zhD zINR|YzR{)5#+1eU-cV3cOg5=L0GxVkQ%ElBEP?#FTWn7cc%XnFH$G0E#!RA2{rf-x z2R-4HdYE2m1>Mn@pTyp>liQrVC8voT4OpXdhy7DAIr^m|T0fgoo@T$Ep+T$iEs0zOXJ0fTVEpTA8jJ#DNdUtDDZWpgKH$btBLEEiU}KG?R? z4H{)_NnT}8qb=N2*IxC!m11tft~qS;L(sc}q?7ma& zZND)34!)yzz{@9ao%c+Gk#>O4ateAf-r9zca_-tkU3@Xn1E?aUqinmCi@GbT=sa3q zKPyB15v|h50)Z%l8}i1uh!&SB3F>UeI*IDe zp_`qKh7)LFd?kcTS|Vb>7g`miC!nC_+=A))I>^T#K>3UD)(1MlPR`J92n`_y98@Ux5!dAKe4XCRi{*wZl3|cn#H~> zln&utaatEGJ*&(vZl)7X1C61?Ha*xOW3{2vqdM!e31Q#sClAMPhq#`Ka@v1>cAR~DMS4iLzdBb4eS(%%!+{Y`g?TvfF(P`@$UlOa`mDQD=5akH5k zDiHth|Hhyk62Bh@VZQ0U8Rxd-g>eu#3hx8p zi|oL$BN#2DPTbRW#xZ;0KC`*U=lca>7a`k>jE;%$RNbq03rPR*RW5Kj?l8bFHW|k~ zI~G#{nlZ#{wCYz#cGCtYvQ2+3yQZzqg-Z+iDo;T79;nX==?r>!Rr7${dgL|~PC}!k zkwgbMsN=@knrF&0M(QvM3?tfLN6x;`gY+WZgxr%5K|lV0#RQM2cp;w0`KA3RAI=KX zq_)ze1xdAGw%slLZ~l*QC_-`;cPjL=6!UAT8fi#RkF@ zFxZst_L;sr5tbf50#s=#KGg)g7y5zt&z#Veu(J@neBV}k3go5ounsf%c6o`t6;USM zdL1NE{Ni12$lQQ;%q#jy9R-%#ACwQa4Vm_K%6hV6qt&1bJzFGHsYns96?D zu6bH|YY>l#n2}{~YPIh#5Yz?`l~yo#&^V_jcvsLcfgQmy4?&(GaL%s5Ae}hwXFL;; zXNK><%cyZM&kruofu8Rn!5agDfDxL|+~#HN%(=q~=~%daMa?>XN(ziX2O?SpqXxKp z)d23BQA0#Ic_H)cv&?K<@K@GXS5O^wfeIHm;`1nHhs*V4RoQa7J9@6R6o}Y_tSafq`yu?q+R3QVihW#6!;r0i*8g@y}^BuXI4( zYjeJup^poCg`0?-DuDya_3$Y|Yobf5os0HIm>YDtaTkcDqe3yU-Xw%oT8t74?KK>lC8lZvtn88Us;`n_Fi|I2tT|jV7h`d#n z^_Pq;imf6s`vT@tn`ISTC{Oy70Vf&~)vbh>&wT7Jo!$^f-jN?B4rmtWDwj*ipFxqK zC7x-<>ak}hi5?vS!gRK3bYx>*tv0;X54>@)2byTK2y1;*Y@N{!4b#hZIl@x!N_i~A zYIzm?!Ve}7xGJreRHfI_>+|dMz9Om~LIGg{&)NemNSH~v?})&p32_-lMvWZD=#XzN zm5_|sqLFBX!txXVQM6*v=hDU0^U!rWn}mI9%=?0u z0ZZDa#qHZVM;C^8Xe_EI9xPrVPq*4>}!b>O2eNTFpD@8%>`D`P1u(pN08RgFL|RY%Vx zvpY-hUiMA3Dw`ZRf;1S z#Cu`s5D}AdwIa~Q+0r&?vvpvwe?CviFiE#pT}-G!niAWZc#u%j80DQdC@sWu?D&~L z#Hv!bq3BEzEnobi>z`8?&CyQN`gN2`UgW2}Fs{tGRxTlC1d|rcWJ46*+e*bwsI8JH z%H*wnbPeCo&lr~wku@g7uIC7?72@jG zH^*vFO#Lgh6e}yPi4VKC8_y+I>L6i#q_>pb!UZdTb)?4)gx7eGtU{4GGez?~ymG|Y z#+N*o2=uK(jyriZ?N%1D)?~sWtc>Jcb zeT!t&0+8lyrT@3y;q(TVQo9IQ@}g#hz0XR*6S85oIz)(==#=`RJGEOBfWd zi7hK@k$=v$9Rx#y=!WeNMFq@mMM7LRzsrdY|2?W z%HgE2NY4PC*2^a{cEda5S12$2EA@ex?M9@bHSkRih{`eda>jg>nHHs4B<*euVyo=< zS8ea}=RvXk`l)*8a?b%d+84dHONPI%OkPpUP15KKYfZI0mbA}@C<45{+?-7DqFTLK zd|JAHbh|JHX*jC#3d{s+KE3QBe%A zQOXRbgI1;D;E(~gAT4JjS9JKQy%`GDq0&Vp&)tJc%c_(jIYGzi!ln6qij-O0iJ21C zt+4ZsJ$vz+6m`BZ5^7GgFhI;Ig@v}k#^NBWb|%5u;b0pbB4d2Irk&Kzra|GTDaT~- zucRc|44P1pqk!FytDFu!6ccd9nasV@vv`}-H%gg5ELCA#Ev zpYVkWMW#%inszrWSTUZ}-r){tK4Oc*-02p~))ykW*Y4hJU8P!;Rvm>}o$<$d|3`=F zE|7DIYFY|4RmZM;y{`E4bpJ;Sx0hzr^HxWC*Xr6Ppk*n8&sbMM&{e3vhspxId#ymu8XF#OJh0P)zHxw)GbS$>5$8boRB7VOaXgcP?o4~jG=|} z%c=aGdp?6K-(hT@89XL!+gIQI;vcK&!yH#0_v2omRtSg3r z>&&!(96I2Q+)df;nk6^J`+=Vbll1z|knbhXI>R|0Iu4PS*%sx(b(KA@iK2T+DL z!;6nOt%!%m%xkt1jrw*5zr%T1Vi*UEP1g@STbmlHGn9F=2i#0&ikU_(9jd4s&`9dO zy?Y8=(JQ_`K$JohV6~R~ZZ1izAuMOr@;OVEo=We}WibfqVGTfz@}?Jp)3o6z&sduG z;E>P~&s??jO@_<~IRB|bOy~mJgl03A@^0UTgDnL$uKu$3#-LhWb`Q z=6~+5nHxAencMy|kdIQ(mPL|>=Wd|xkW*D_egxv>2RBD^`aMNPj}IRuUOLxJyd3m zz&rirB*|SxZz_W_e?&k$luAU2N0AAqavrW$l8ysI02=+GGKE)rE-T4Tus7WT4R`dO++T@(&Sk+;BM^7Q5=b) zq2_D@d1+HRn%NqmJ|p~21^NrH#+oV)_d)9eMxNe*W!Y7zym4muj{kxQw(X2~$Dahx z>2DJ}s{b`i{*m2fsl56kJtKHqN+wgG0z#&)>rqUP$5RK9Gy(&K(bg(VxOn^7W7Q|4 zy7O-Q-;zw>7T8&nC!&pzOW1lvLzF3c_ol@a1wFvz6IM`qWA1< zEiQS)%$S0m(Nk@z1!8^Lot8IOv5+8$q#80ZFQ`gdLZVQBh7u@xHk?pxo!X`Y!U;yT zV9&geHFqb>9jXEXXKkOWxAHQ$swfDgsI1Cg3JJJm>a^#V>Eh(MsY~Ff|!X(;Zg8TwnS&1vah^ul7@4~nns()56G~~XOJ)fG+*TkUVBhmoVR>Skq z1{GZJlcS#72i;B9i7~M{O@-`4t`4aKou#BBAXt#(D56?F4brAF;94??^0eLLFua+B z)1#v~?00I)%&=Y;KDGeSFIUPF_uNzp*j+j(yvy=KlQSC!4+3Fd$mnvm-~&h(B}S~J zLR``O4C;=nB|j^lm~gUov4|>K4av7zYE@R8m}I0mPuI;6aV=q1kI>#`DuG%`@M0`B zH@)KPTX;SNzxKM`{!?+3>!AWj+--#|pDFzKuDSOgyhZ!oZax0+En(z!D`}RoFYSeZ zZd!d`RVtstggHyreG3))R)k#nG4Rs|V?VN27e`RwDBfmgXf)%Su{)ZJz>{=rwE`E= z6T1yIt}KClNx-K8iOGY>QDpaktmN=FCl$gs%AJ@wX;n0aN(<4Ps>Uba5z*0p;1%Mw zJm?a#_0JWCliL#<>e55@_i$y)+nWy<>Qntv2Pyg9DTdl(I0D`XLDt%Q!ZuG7^v<{Y zGG?Jr=D!0dlD<1ivoBKiU(?tDH99?=)r|9luNMQ$t(oXvpUc;UG~sVoZIv*Ug|VC# zfL}p*iQybOhz6&wF+d1hahR${WA-7#wUxVQvkr?44R`5AJW!8*eAq36$3_Oq-2lpN zD=-aj-lHL1Xg@Gxe^Qij)k2YMRZo*8zivp-ry;$jZ6DV0AkH#I!Rr$hPi4BOuehJs zjc}QIgo=$Rdtu}0Q;G+ z8f@Gg1tgC|H_1B@!JZK$2u!&(hImH-sS`15_%gESYql9LsZ&*W#}t+N)TSorQ{|d) z^&kv`Jd$)T=AOv6n*OLwtbG2U01!uoF6xQjWuDeQa40 z_ZWlsiCo@XQ}zP%CFcKN8lkbh2I!>ysp{_*KtXxumN1H`B!S@zspot@s^g;NEkBeo z??-TDzhRKkF~I;07T^}aZ&aEU25g^#iZBp{JcU*4ypZSthq&1J><%fdAV0^&cx0qR!i8l<~S2Mpf3|(f=ik)2g|GBhPJDX2$RnSS%`DSPwsCzH)mu!HA2v+xkWme<4 z_M4wmgmz>u94Wh`Iox?Ep%OUx7u&A@<(zL~J3ntuRNB0TNWxP!R}4}SL+)D!15+G0ynmrkBY0e;$&v6?5L*q z4bAb^dIianfZARpSxOHvK7R-z`d^}U5h3p4)~$f;$?Mi$=(3DODqJBIn;V1Ll5W8j zCK{;^ivkv)vv5(!FQ=xYM{S6b*%jqRTE|#;H6aENfw)&o1~mbd;Js_Ozs`b>syNb zj+Smd%c4{{6bDaNVh}mn;x&7}*KW|%3TU?;x$uguy4%B=biQ(mAZO&=k6)i4u!jrqd&&Y( zB>lWCqTs4jIoK%Uknd?S`yS}+{iP#*dsmWIwUJp+cX2Sbo{Eds2 z*V9FF*R#0==ork%|FWB%{=2*vbmjQ*1dsI0Duq>Ann0}R^Vnpes%yqFIUE|1Uz zY`$br1QQXQFV_LRmkLe7cwj^@J9SlYscieuKXJ#^mEQ$k#3kEx9b@sHO%w}k(9*_c zI^B|W?b-AD<7=d*2Y@Z=n#l@@&A211b`Slw5V|DleI9bABltj!6IWkZ)UPc0k_{6EC}Q&X(FNjY!45E84Z3x z$I4*Et{$T!Msz7k6-{{&GnX*MFHQM=?9{jqLLj?3T-oavFPE0qX+_21ypuc zpuLXc;XW5*lc|D`iC}j13$o#NC6=l4{Vukj;*vffTCUA3k7K2wbtx^B!JdEQ?gXv$ z@d79z*VRfn&k7!RJTC&Mj}kUXo;1FiyM{7dXL%pgMarar-uBVy9)$C~HINFEwgxy! zww4OXfq=`#E!&9(hfZINFJj%COcycF0$(U64@aKDM}34D8Y#2G0YJ*F3~>laER1HOMb>l>=k9d&Sh^WJ`-97;M-oc?Dc9$tPoAVUX zP92Y_zn=|OLWq}%!=YuDzEsNyN~=`&Kv$(JsxsmY`ZJk{p~ zD4SZU2q!5(D7TKhP7G}+cAHD{U1pVhOLdrbsy?)wp@QB91PFySQI_yKKU{i&G8c)g zBcyYWex8Kn4dH;a(Zc-i#k&U3EQ|JYXW^4op(Kl;c{x92F5`&l7sutto@}^&)P@Ed zEmS_<`$)1H(Xu`A6U@byC|@tjHVdwxHmIwnK9t4JMAO%{<-@Qlvx9OpkXGB{t)Do* z#LKkZS2xE)-2`m7XLxJ!%q>7Y3;M9r@d}zP-C=%+vvJi2FH>yIvaI2Z?>-^k`{4P? zfO*L-H3tq9Sc1z`<$0EunSz#-Zf6WU&q5N)W`OzjMHFnZYiSQr0lha#wj!5m53zlE z=l!G$8N;^uvjTeN;P#HN2JB4SwOIq&h;5RS+eVe^OjX7XS>0dWCtWnP$n)V?Wtj%R z-tUE-fBiOHfOi)tPCy@KQZ0(H0vPtpjB8fhBbLq53h;t&w+pwVd%OcD@W+*@TSy(o z*dTh~&KxT7a>Cui?k*XGE2LADAn?c_N2Hw(MJb$lvCIbeJ9fA$DP^$M#=jj4%Xr~38&Wt$N4Y~}rm_K#TV z38Y7J^7UQp%9m@>zn4+}t#!+P46p=kZA{EfogMW5ZvmW?xUGn#j6BkVCV)5}6bMot z+B9#mIv7kN(5Mj(BTi{8h$s#`enO9?Hn3cqvAWr-^htu}Br+Tg_YVA4fIYLh$ydL@ zbx+{wlk>XjIeoPK`QZ+w2Rem5jQ%@$bJ;BgFY9EDf_Fjsa^q;T+Q!nen_B&7Mx?{k zaiw+=oe;WA^)1p8$ELaIWtZxG)Hszw2~ML)r0#w%S7F^)Ott2B`d3+VDGIH) zIBnl{di7gIHpVbsU%#VOvkd3r5*aIMe7aALELch}<=nH$qDu|6YhMoCMttJM92)XE z^KM0EqR{m<$nTO->b1Jw*~W$1M~ZzUSkNeh`_=~eF-&@MNrQ7Hl!Y06`yd+Efw|SQ zAO3aexzN5FpW~%%R4cA12(M}^zml0Hq>1+>6sTjU zLPNR!S<}{Oo=wj|2#z*&g!3S0#|BFv4ja)`*e<=FE$XbUx!nEtRWeI`!5MfidAlqmysJN-CXU#*!Nekce6V#ZVa(@aoPENcLt=k^0zIth+X+ zHyG3{y;~s3w)?2=?5QH&4nCfgW!l=k(~4}Jrv=Mb67Fkw{F7X8{o-1_?F;MQGy+4~ z)C;U%_ah`R?M^zw$sh6aW5b+J7h6VHtC4&&-fw>ccx(6RK#Co9@N--xP;G18A1fwa$ zCee>3BNtNsP=^RmDl_o}5hMM!n(SX0%#W!Mn~rV74E;OaLW79U1UR-Gxey-gSqE}H zHUPOFpI2c@mWb~NDE7KDJ?pRWb^CW-{nW3{2KnCtpZ4!a)PDe9*v;6``TsaCB&kAp zBCVis13M5$=p(V{B`fJe)OVH^5*wFnePbO~p*A!CFETW@f{SB5GYbSXimw$~$0uKD z&XZc3X|%62>dm!6Xp3iDdHPECWIvh^M-6`4y?Zp@@^oBroawrITmIDX1nzZtV+|FC zG$>|HoBgffAt5VeX?m|^Fg*X;eNzJ4G27ep!D)`A3LgkkC3AV&EUYp)Lkc=7XL+I7 zKY8n8an#QDaW3v7uTN1l2I;8qGyP zGo@NCL*yrqPBSc%tI{Op+Uj8oSJmgXtUqrZNj5&)JWtex)zo&5TqOI6$(*mbi?*09jV8NM^q=~7HK@8ND z&vN68l_s#o2c$x~ep-k$I0#vnnjJ^D3?&XWL=24?H`-IU$*xUGqbEQj0=t%*#w1c} zq>DwBSCC3Y=!Y5n!9?|ywp8I~P{E4m*^t?n6snQ6QfCGs-q9HnfA8PO^ z1N!Pkvx4>;bv8178CXOHk6I??d^wa28AiXj>7vvG!{8bhvbpt!N^QcS^%sfd34w#J z*ic7ZLfg6N*o=SVlN)@8_=yGlz)+^O)Va6mf``r`TVNODns&wnQW-YQ_fHUHD%|>*U9631xSLio4|(~i#Hz%72ThiniprGkUijgXBk+{Q1)`uY zv1p^bdn7jaxL0Z z{Zc(2iyibQk>6wJ+Qf^JTKDc}40|_}DoYT4wsP&(MCPK^^zyU{F$hk!>McayQc-fX zG4T^=PrJTWZ%M$Dk~?3=3ndRxtTk~x1sDen+1#;`7p`tDC_i~Uw<%{%E#%k)4N;_z z_)tnv*im?xl8!7El1O@aGyS7~IGQjYOtW}QCLL&lSy4sKpv6Svo^jt{&0WSWE7RNQ zXMJeCYGrrXo^syCBq=k^Yp6WATl?5g=}O)aItJ~NH7E3x z8}7cCYt@eC%a`o?bs;BZps4ykulwV3IE$5mXI>v5XxJ=Cr04q{V(Qe{ zvb9mW^n%H~#z!b=Jc&9vtzLVyF4!#;XvUS5&QQ&bWwTg%>MsXMDmM6z2`*d02isc{ zcvhQ7c_z|UNda0@4gf#m`nu@Xjy=ZvXlLnN=IM{Hemi4 zp{UGjCfaRf4)yUwY}n~u^YVeeZ$iW^ zBJBJYg- ze9E0S`OXy%=;XkHZlWzF?aR*tR<0h(-U%rV_r3s)Y;FWZE`|BfwE^`>^vEF^)O z$G?O`1dT)^Tnoa2I-bgJ-QcXMkFgPchk`ET?Hzp^jQrhRy+6_m*ouH-1_r)fwmS?} zJb?;5bHvpBxA43%u5OxTg$k_z4Sy9Fbev6$9+E=#nYBHUCBA%jc+K1j;cZ>d*kh^| zaK@=6K4SWaBx|k1cQmm%If!lY-6Zz5b~mXq*LU*GXu#0OFH^E2%O${JJ8Z;xZIj6Q^6sgRB=E;`=6Nfv51nLu&4KRfVORYFQ+Dy#DzxBi+9`b~5tqoFmrpcOKzZf)MeQGfnzqaf*ZD!X0Mn))xrX z9{!URDm3nK7?i`DeP=jaS#d^nFq%?ibJsmLL)YAbDiZpbZLMm{d38dM=-A9hczOi_ zJrLVnxOrU=-@zPW2*M}E4}nd3q$etV1g8C>F=;)xZSXR^PHBCtrIMS#5b3_~4Ezt$ zZ79KZOS523`S}NbLE>}C036oYS-{Hl_MbMkAJaqSx6VpGrkLk<6q<(|_UgiotcD%u z^)~>@_N`ma;Pv9otwheygmDX zbNRlWqBq|UxPMeRPa_5FabGU5)JXqY<@{&kSe(BjJBC(&Z*BUY?Sy#$t3Ts6_=n%6 zp_8Dkwe?r`Ny^;D_^X6+`7$E?-wM+#<#QQKespf4h!cq}6a?$@B2~4%C5?5;#l>Ig zsdAQt1gAZ)=g2F)0?ESXlK1Ktcv5SHaI+y6FH^L_i8T4VF0|WTj?>T6&;!@JyguL6 zhDE@=p)FB5O7AFHVS{vzM*8Pvt#qm&HCZK!yVXnCSy(fxB-$pc0xHeJs=}SAtwetj zkV6-UzNMa%*q}Vb1QF@85!^FUyMjId8=lOhCZAf-gY1QI1=K6E!&3sGLlOmk4@OAq z(WFBQ%-Ro%*F&FCfz}y!Tu;0+k+X-L!W882Ja3$0G*R@nAs7Fq&Osn7(TIF~Go^q8Za8|$-Iy+a4Qn#}FVY!-Vc z_#iS^*LjbyR1reR#=gN9W1xB#ZSA{A|Dr6WFZAE#NB=U_@+kj|P;FBc# zjcCUc8R9kwUpY=b@W(gv0`iIww^6>ZXp&4na-U+L!?Mu%>JK+t(7JGYGy<=;)3Nru z({qZ=8SrMdj%>94!%@?$xg;yKPQ{Vk1bzpReU66li=+7#q~OPJV3u3A zi_X3x8SOy(_2x-ZjcLjly*Xx9nV={w_A}S>H?WONy^RUwM=Ixa`1N8h&7+Pk+z7;o zT}RTEEr^aejI(DRZTFl+caGt2-uy2y;0m%|!m$9R^}_72QWw|cDjHw#(6e0Mqr?g`$scr<)u=4{sv>;udHUn4Yq>Sz zUX`r*E%BFnf3GI}F42a;ZC{(uMSOwM=%E*|W;9p|xh|S`j8Z{9Gn6KBX-Z@wB#9E! zF?h^O&7(9G@5`(Zxck$rG?*?kI!Dz>n*3dXm>Z&Xoa@+tM%F-Dw)2hoo+8`}gnZ9j ztAy?{nqg`*#ybi*|L3_%s$N#t@PTo6fESL+fz2r;k2Mbf*D4e@;z(1A2tH z8zB6Q3iznqQ`558k0)QV*-fY4ZdYn*zG;ob5U!z{KvU(!ORKLcCobX+;)MrlW1}> zSrH=e8c|$;!6B&1l)RbjdZ5I=d{<^XGJnq%_QylWR9SQx@(fH+H-TBRuCaV5*We^W zquU6z;NCX>Nqxp;?>wejhO_ zUOtEm&3n&T;9_x>N=7V%KJ-yoiw8I}yf}~w-5|Ev$a8HxCA|Dy zCs>h!Y?ezghb$^;EwMq|q^By0S8#|DwUhIVdFL$JN{jN4_>Y@VzfG7tD0T>{Cw~F; z1=hu`A?e^NldDOPo7C?(Y6Gf--9~JxuJef9!-|x)CSlE;I1g7RS>`|y`|2sVKg%U% zX>U11G92lQ7^KG$(Y6ov++o|(KpqoF^|59`@wGjnswGRok$8swF9?_FnvD1VAbiVwwF0*+<5h=aKy zSnVTXx|3r2nH@&!17KmD2VS<#ya zy^Bgq=tFov5dCz`W`p6IF0YK>f_U+jK}valfCKsZw|cj(x&F>JB6O>;SR^*@UR?_O zbakqF*)zVUu7Oe3qKyc=TxJ4(2BZ;Ct_pQ}ayU;MLANSg--jGj+8jR37wsSMv* zKpgz+8R~L10&WiVCRf^XwT9^|A2}aN1oswPx0KR)>j>OIHS!CzycvVnWbKkA3iPF2 zu_@Js=HrwDR!!1Q#8@gB;Qdn;oiq?F^$Z1;e&z;K8)^Vy@A+BUx8;+)e{6U3?0fc8 z?Qfv2F@4>Z9%%R0bviB@!76IIFWcsv51*t1a&Ox4i9pCu#8>ntdxK1TD{-k=voI4} zB*SUFOgV(&bk}7$zB%J2FdVQvJbZDa?buE7cj{k-yNj)kWr%D23xnPvg)yy;)AsXw zTW~{2V=HP@hAne3lfrXgfu^U(xGIKvrKoDg7oQc7@4m;)+p0M41HAv>HWtVDBGq3V z-03e*kbfT}|4TaZFCmfN!PMFM%TQC;&CuBH|8{e;V)5)f1g?~Ba<3oxdMs0vZ zMu-Lw0ECbdh63QPjF}2d&Xa9`dy>fz;e5XFCf4DAL?OccneBdjxxRka-R9NV{-(7z zD-^v$nV2n2bS9IEGfRQ=M{1tjVBW>s=CL0?*Wkjg&!#X1Op3T=hBg8b7ZS?S`?;`tlS(@ zA_OF@wBb-?^%A1mJAD#u$G%7Our4Yc(>EA+;T5V9!Uu5+R^?@7cbP1a3ht33Nf+C) z&GB+k3H6cYa0@7u@Lyx(U@r0s&{LFj>W}3CSNhFs$Bq~8fjAYSWEdAt1e$%5BvPWU zY@^gF4J%Eu|2V)`YnDW%FP)L;SEl>-2gv$gWx0Pj!2iS}lfHClUkBHf)eF*d!}$UH zCpQTm$vAK@my}eJ$?ryI*g4s1Q(^eN<#`A0MifI5AXYe67gF41`k3jses}x)2lksY zTXP?wT#PZFdjFegA;N^*EZSH+2+4z>45vLZ0C3;hD?`nYNFjj*2~tj!48UYSm<{Oz ze^2~*IrD)pSK-ck(`BI_0Ixmry19>7y3zfTTF8ZJh&2vU{d=t~xsO;NZu%7>v4abq zI!lb$&Z2%+qtsb(On9eRyJSU?CtYM>B05Si^B7f8gRv_k{qeXkMk?CAmA*#(*}xf- zW?Q$7?pRr?T8gVDzJ7cL3GV)m`6Evqe>QU7`Grzy(~Z!(b3ZSi4Pg9eWuXq*xMWG& zVM~`H0RmpxcTZKmh?WO}`s++d?!mdVGz%09bCn5S6LXaXpA)kTGgdq3qOW@k@8sbI zi~Z%FI~KUvauTJ!4y@yEg<(wpjRTYYSC}blsv@Z(f54)V1&a47wW(F82?-JocBt@G zw1}WK+>LTXnX(8vwSeUw{3i%HX6-pvQS-~ zOmm#x+WyDG{=9#!>kDiLwrysHfZmiP)jx_=CY?5l5mS`pwuk=Q>4aETnU>n<$UY!J zCM`LAti908)Cl2ZixCqgv|P&&_8di%<^amHzD^77MAEgHZ)t)AHIIXIqDIe{yo-uM zL9f=qnO(_8(;97VJX}35$eJkyAfs`;RnL}rt*9hz5Xs|90DiFC2OO@ZB?l!MdW?Y! zVeW$Z2knWJ4@RJxr@0!9%l(-MHk=DYEl#4ev6Ge_Ebr~MUtrj*0P32f95h$u7#2~9 zhM|KP%(!GKDydv2y=;WeN9p1qJV7#xf~7NO6RJ*n*61NJ)-33TQ{}I zRJO7(=F0iqd5tRKCuN=Y>ce7iLGXL*r#jK1o=E#$hpC0Hw5mjjMX8T9T&|4Dal3CO z$n^Yq*7KP%JSfbV_NjYZf{9-%L2-wibG3!?PDz21yQnBSK{$cw0aS!b(~MH%+@Y^g zMbh^HDT{IkJhPp#^C~#|0yC3^d5Arm)5NNiSpq25j%UngFeBVnu~h> zF6a63K7QC#d~?Uq-H#2|W|=~t7C;0wMBTC6W6CFDxKLt2tEh74!D7i0?eogkWEP2>jmm?Q?6ZS)p&ZkxzP?QLz9V1yTAnzUG107^d4Edc`eU(7{J!5-g|<@s1*(lgQ*l63GoeHDU})F-AHL zvTY+9qB`=3Fo!*RAf{x*KSAfbPOq3%0h!l5u^eIT#VnZj2b@r(B}rE6_bCSU8n7qu zdec9Hxl#li5;L|xqIzgWajIz_wSJ(^J;CDo#OQT;>isx9bR#bKlQ`G@hyd_j7v0XU z*FuwLt6w(Lu!EGE2Wj%0P4wtqSqlayo+lvv zvIwLW5a2I5Wvx@<3FE9`l67?{Pqta37`H_2r~Rh`mvn?bJK@;O)^qixzSP z^P7CNTSUwq9Gw)M4gTZjzl6F|Dw_XLZ+{fiP*YDRx4HEw)6&%LXori@JXVM&1&$2V zCl9%_tkT{{zQOSrdbD;S|Z<8bkmY!{JPNXC^QcUh(0cJobNZ#riP{Tx=a`7jDT(xzwJmnVm}Q6nGa zT%9oRYxj^klt5N6rBVfWzD|HYra%E#V{M!|U{lqAWU5u;2wSi)CD3xrI}RgWkKKi* zt118z~o_nKw#_j#v?MmwVR4Y4%(_3PW5iE|2cLH5fIE*5dkli zhMU*G#1uhwUc7sWMQKdYx(}>KKo5C^Na{U&-}Juh(tJ@rJN|MpKkE-g*?$uEfI)Df zEKxb*aGUWk@AbOG4U4la2-@}0F=Hic3Hbt1$B5!c5KQ?(k1sgs-0D%@;n-Z!;Cq{_ zBxJAabMsyPcV@;G1Rigb1OIssZO!;$tnF|9-D0Ch+6n9!tdd`(8ByDFFBrN*Pw-ox zcV*7Bjv^{JEh7HuPApmjnY9PxmQ)K@DFj4j3(eN;VU44QQrXUERI5f0;}m-Qhavv{ zAo};V$FL>UK(bU-j-UyFc?~OsvWG++(fb-0aA?&mKI!s`30^Wcl%YSpWaxX6T@^c1 z9B2^VL6{LQH~s$jJ$`4p@eN3n2U2DV=D-vsx?58lKAsCS!SC4v^m0uDX+)@O*S*6p zxE&BJ&X}FQ`&WGT8o3PW#xq+Lc4Hrpp9a6o_4GuWGj_K@^PZT~F*)^q?e|>&QQasO zz!YVY&QCQ(D0S!VN*Dx((~2}A$YsEKa0aLWn#Aix;u5Zffc7dqF+dYcNSDBMynuIX zQZkv0a*uw4IsVMi4?Km>!1qz*GL=a@C11c_a3lYTCN&~ZuiavZO-Y(66Lb)0HNv#0 z`wt#_)H7j8^F@hB{uZPB{|#F7uNeJ{B02tr&7!1#Zk!nTbfl@$f&xVW!9zeWr@{_> z5%40FkfMzLCVdd4zSfl4>^b%D?OmojR)}P75Uw|bVR|d8=oe5MQ_9BG^z@sHiHpnQ z&dkjAw<9|`h=AIiRusuaVRK0h<~pLJrt@$Q?RJ$i3(W|bDpI93J*qasul!Ax-St@b zT70z{Z9$Ac#uW+8Hp8cW+BEZCFHLQE003gFJgjd6bC(a>_%r4gt1PIKDxdlOmG5bxg!q%}OBBmE^em zMD$CGBvlqmJ64Hwq#{I&4eLk+K>MijQH1o}Sp;1j}*B%iMG#<^c!LVvstF3s)e4ogyjcWT?4>;2{JEMM^F`i ztl&9)S?Kp*~8M)+^p!-&4ec07Sw$10W>b#&6n%ipaV=_5%8df_LS_JKqMhAo?C zqfLGE@2z6ldhp zB1D>7Em+1(_>RhmZGt+*m*>vO9G<q3-DZfdDKlO|pcqDz5KKociyxl*E4@0RqM*whqSsCQV%`BALQ}T07Xe zv6IXT6bWO|KoSQMh10z?M!+PW0uSf#1-I1kgk z$8cTzXe9WR9(n1HVJyrm=o%KA*Hs*XgBr zE~W$D{Akz4%O;jWEpVS~xHMj`dsp{o#$0+@dXX+_VySrh1<6m*YPkmw4uPY6vJ5|> zk3;DJ-lbq(C$EXJh2z*X?*4$HJyBVmnoTqFT`_J95tUE`O9u=LU;nba8?|q`5IjUX zI{BaGy-liq*$IgD_s6J_j=g@C%d8izHOUrg{RJtXW*OPMx*~M{ZIa|kJrE^ zZ(;A+Tvr91Ir=~(%4j6geD?WU0);@_g?gbbo=l=iVVjjY6%Lr~YRs0YC@-KA`pP|` z>K$Ca=mj>xP}M+LwguRU`7>bsXU^y~bxIMUgGB*h|G4G2z9$<4Q;6eyG8fq)kX@0% zwGHQP*A3~Cf|`RB_Ob%FYqQb4%8MAsKvVs9gj>z9HSWtP+@(LptM+K+Y_h3aH9hP# z^Q90YIiG!q(x%+4Vr&>svY;)Z&Ew@1EoHHo?Amx~asX+u?q3v`zgzS7e&fnR$>20R zrP3L77h8PI5}d&I9(6aP{E~wyCdb;fiS9$(;^4JnczkSvfXefJf35vR||0K|IC(?ottwQUIsMi9qL-Ki1PC5|H3*{%XN(vI#!0?7F?op25ln65L)@Tz?(<+kxO<@M9G=^I#=9#3WgVT| zbl4nf1a+Z@&odHk*mqzIJ=?%Y1ViaVpn3@R6~TLbG?~$hX}&VYvoWg7VH@-iPK$D+ zp=cy^wSS3hojkEf*hOx2F4Om(YXd10{e&yT!%sCcf=xKZtyz{x)}4C6it(*XMQ>&R z4Z2SnR+GnjToyoV2iGEZuo%;D!GfAc+?So=e;}fkPp_O|MsuCNM6*e+(Ip-I=Dqy( ziA_?>c;WB1-#U;9w9p~7FQuA@-mRyha=^kiNVj5_bGj0q`62iOw)W2<$OZDt_U2bw z{RZ=QK}G4mA5;YO9gV*%aE)yo&7I6$j1|AWUbHd&qQG|gUmDK;vq(qriv{x|f0(p5 z6$f zH|!s{Xq#l;{(2gCeZ1en^x!yQse=Rf;JA5?0vLCro|MS13y${dX197%bU4wYS~*T7 zNMPGwgSIU0JW2NftQ-3$QXmuq?@1Y^@`;R^fPG&PD=ww}!g($Q^w@U%jh~>J&{$ zIT8p4^dD`WnJ_Z>t>mLFB_6}o5mz%Gl{ncGYtQr!*NEda(Jb9YovwZL-9Tsg=!3Nl&5$2Pez6&4IAf6x^6Qf=1#(zvhhNAUu7#{N>lx@!d z+2KhRXK3(adQQw|B#w9(1`V(JO-7w)D&ou3Aw-!D{s&7PYIJVqQo|)uLy|#Jserq0 zp;ZCFc%J&KZ-~*Vm$tJYJ;QtohtMEla^-AW-eR_`_ipuJ`1HUK?hs)m#r%vaUS-_* z+@<QOd6bSo61=b|nA%cU98n%d+|}3iuZ( z{8|y|Wc(Kyyi_}NMOH@r>?#ywo&q)`n)@kP_C0=jJ~z~WUJzu^3|ueO$e+=ys6z^p zQ`uVC8K^aSoto0do?vf!^n}e&Pbvi6emgpQ{|E0Y-qTPIUsp?cdxMi>EfTK>n^V_= z>-GEQVOL6xug5j;H_O{Le+Iv*Z3DA0iX zHb3Sb%u&(Yt_VcM08@~gL9&uQc)pu7mkm)2gtU2&;d73)p35qTW<8pc`u|WSj&}5nCmZjz<;EMxr zl^p?8=QuuhYi%?t`?^5`>fPlcL=?5&sw70n{tXS9I(P(|C2?whWVVPPS0gYFXU~@9 zjC{H9W=#m1rJ_}^$ACWgAJM(d3YQc*^yKM;$*UHR#$ZkhD8JM-(W{;BZY2Y$wW#bd zXwlT>OFC98rxTg-En@tsKv>>1AlkY#AIY3%lIg3FTe;NcQu9g5b*&bcsIrzU=I3#i z8nu>|Y*v(~l$yTfiuZwyA5s{)-d`;s9gLc273l3pQsn#yLw)m$zh;@hofUhA5iV_S z^Jc-XQ>~@+cQ!jTYg5rv2lRKSMbRK?+T%b-otosVU)L?64nHW3X-F&MiFN$=y<94o zUQldpIV*N1p2VbtRH9#Kj$p&r;g2e(ZcVm;a+wq#hlUi+fEkQ4c>2B}!hY0BP&*#e%)U|_eQgXde%vfhiAhy&HT&-bI#pprT2RHl-n9Or9kKY@ z*y6h^2Ln;NAa*rkeMxTgnOJI23y^g-A!~?`3V~4otb&p;eW9M5-lobP=P*BL2RaxZ3%Wziqya7JN{_s8TzoHXh3ST@OSRX1e6 z>$kR7wI$QYF$t&v}!NXCxg*MV=COu(&$S|cT(SuBvRZ&%%PHyp%;O;VXhH_;x z2HE2!upKD-`%LYo4-j(^+!AN!uZa;`%`G%%&#FDxOtExn{+1$mp2Zq&fXt@IQ+Vd5 zxy8=T8HbuT)*Nf;;=>yVza}=`u*qPzR-qSAEnH34$p9#bZ^G__*EM(OsuHn9s(iSs z@1b-`{6L6cDAQp=<-~@Rg8P;+;HJIPnVAD4Dh;+F&&1@R@G%6ml^W!^W;MP0d)imB zbBq?EBbgVY&-X?b)b_aAoKZUE36E1#{7!D%s3ckf+ca?KU~yW?7Cs%}4bKpA3#HZL zY9w6<)gF>&;-Yp^>p9k(4$X1%!Lb75zWg?uNWkgi10?l4%`F`Zu-y%^bv*Eb-G1bx zfx(%lYkITUQU0wktRS*;%_P0Oi@k^)R&}m?Z&ryTJbM7h6wNb0mMpv9Y>ilHz81R| zNa)#|zlxlfx|5EZ>g%QadIiiL)E8+5jg3iqB0IB;t?;L)3$_{phsj~;UI0o%gKX0g z(gwmaY_#YBn3m`RBz41p#ldnxLp79&YIMO%dpLkd4_drcD1y-7of@f5?&C7T7bg!* z+9O$vNRgMdT#m~Ql>Nl~UZcEw+Do(CxnWs%MNl)erW)%a9eV7n)cJr@N4*@WH$=Sr zAhZ%9vs<41`&UP6;T>@`?np7*dBd--?u-hXv~`mYkhSp%X)aEIJ5@3x@SZdI9=Z7^ zm`a$T8G>!TbmyVE+@a)*=B%I01?eWpM`#8RPKUTB|8^2_5otvAK&gp4QmeXLlLl8< z7q`?^RRNV0Zx>wC?=eUpiywAApVgW1 z26PBx#Gj)=xWi}Wm@kzi;q}eouVi_z3bwY7Et>>Nthd&%~TRU2RklNMo zjR1tO$Zmf2ikfZdY{w4qmcEwuj?VBt(Z~4uu{D*;?462ZUxjtkN26g-Mx^A|7~3vj$%%WKOuq#P1%TfMi%b5 z3A+m!PpQ1fx`!Y4u-@>yAKa9?1&rN1_!|NmOYN}D@6ev!<-68YDd`CqblRnk9+=E&zlax$$Z zEo3QqIOH#=`aS0F!U%onRIz#%d+Uu-ZTV~+KOW5lgf3#92 zs=j>nz*M{C5^SxuTa3NC5PoHADLhR5{6QFiJm3{lXa=#5F|Pw|uTB(`gmtPyy?-|e- zo!SpO%F=zX?002uubhHWls4g@ z$#c|C53m9UmMZnqljx2rvZ|CtTMy21QWa}%;DQqL1`b>3BPxm@4VTtyDBge$=!Puw zyd&F+VEvOtPlX2!>NBKqg7?CC`V+rmZA=K7Y?*qaE@CQvOWin}e)41=!WLN*AmICp zmApxQI7fZ@Fn$iKs11M+Um$0c@jZLYE;LiUT>Q z;mj4M9@HGF55B8!suGMpT5sP$Z0H81g`%akXopX=;Vuyya|V^5eGs80E$GcNc_7{w z^8xFDCK;Ge+b0TnY01uz&_%fk-3~ zvi@tUr$)PwWk9(8y{S8#NB)r=Z&8RFES$pdKZz}*U-@kS(R3c6ORIFKDCtI3bCeVK5Ouo`CNgYaXVC;;%_1`Y%C zS$Gkx5qw1G7=P5+GQv2jWqBM^c;nED(khcK>H|id>bS}R(2;{C#FXUv_o-0C=w18S z!7fg}MXAN-iF$lV4>ADs{#}r_Pj3`vONGc>LbCQ$kqa~BpZsXaR3r4-jfEZh6lG;g zH2?O&x)$tLCc6%_^X-$8UCQbq`iWZf3k_#t`>d-3RZ1*6t})5ZW#k?<7x4jX1;FIv z#JqAvG!v>ArA>Oj^}~zAj*s-^uw4QHo?OwxadvD*vQw8q!$k+PkzQ$ck-*m5V;_V^ zO&2BUt>Gxc!AIbE;ki~+_O#~NVhaYQx6FHt%&w_T7mmi9xrCyXhJ_PZ`?rYlZS;Gx zW*VdJVQtk}tC$DGfP9YCu&PI)g+*tzI1J1+`ggxT`r>R1{5ZK7^vgg50`)~XxH#op zaFi4=I&6N~23d3&(`fqN-9g-AD4TjsqHwXNH!B-hK#bOSvK=vpVyEh|pjvqg?2bX_Aq~vcQBK+U4{r-Z;e{M_^DgE#9TxFsI4gL-&iiIYv zc6g{nT!eB$I+&D&*!`uP%y|6Qh;DOl`zGXO4+>ozdgcSKpd0AWrFrJpE8_Np(d2u{OsCVzDh!qE*XZ~Qkk-UV;Za2i^fWH z4GBwmrBGEgJC z2615hax*kh=rlN!7SVm_!m?!&jd>4(rm^_RjHa;s7IJgmpKidx6*{aw&1Vjb5xBy0^j5%jkNfAs?F~Z@CFq3O^wFH- z#IYRF>aR{2o|F+6=`?(!PHgaN-~%e>IHc&2lxTYNE~aNaMm0JjWHoW#EQ1yr@uOXY zKBd2o6w+Rpm!V{ui6q0wL35|47?O$R;hFf&*I;d1L?g;zf#AW{5r+BsgjI9#8$50~ z&kOiWjaUVk9(WcPI%tIn+M%Q%H=Lk!9ECDuUV&bs)b8?PYtO4@A55o)1xlN-2uVDn zw7Ka-zkOkWep`@x4Vn~s$4_Lb3lX-~ySpE74Ur15s#rZA1R#rs6CJQyr_^D_>jwn= zcz|gF9BRbkd}iENr&_k%#j~p{}>)f0wtqOec{LNZ}B7YKgG}glU<4wq-_`Y;Jx=- z#m|G8r1QKMaQP%WN{5nEP~iRe!q+7D+3nU_iCn2Xt*cmrczfZ_Ai{uof8r?v&P6Cg zbtF{QyzfLBY+bXDRt{rwzUdfr1pT~euQjifNXm4`tZ-zxMXMN(x6U-;z(sYho*Way z;!$Zfczr8%YNuBT7-k=DyG^RowGu^y(QO&%=nRCdBrv~E$7_y&?K!6DP-#b?a_ojj86^W z&>qkL(X+DkI^|n^^#TTQ88cjqV^Ut;YOxE@e{|8suiT~=n*p!+*rx42!=v6v4#vEx z2yh*NAiv>w>={9^8@c$;SO)UNrtQ@wk3hM8=^JP-igxR51Qx_72dHv$GqPmq4 z(E|^Cw3ope@#CReHwW%Uu9gg87a=azdA81=6> z`d6FxKgOtve;L#%YBX0`mVrV(g+b2KHd6WQh%WsAkdlHhrDA&huJ59dZ2q#D_y4jm zhw@4ilE@F^?d>rVI<`>-2@eYn*~;?#ilJ$33$~s)JwT~~(t_b~cLBvDYyCPYDw0;> zGagu>E}CG;mmJIf+ZGTtbti7W+rR}dq-a}+Mjlo2dvDV*=L6q@e<3DQbrv^uHWOTi z&XW0)=G8upEJW2Hyu7E*3-&)Eg!Y*Cm!1c;5PiYrE7+NQX?p&Bh50|`)Bk3cp(Opqr_p^(+Kr9X$+rnLX&MeW5Zt-D}b4V$BS=UJD|xt*F3*Vo6OHIj>hb z@3>|ruWGipeZHv;v_nka%)?nkn}u6wbHLaWC*1+yr;4F7%a1vPd*_LPp&Yfy2+EO zBsv&8pr30tVSW-^u;e(0PH!WZzc2s2DJfy8-d^JeU)MhCJxZZUez zJF5P5ln|;{3z;aB3sH*>7p)^yOi7c|Ia7nlM^IU^Mp>LO^y*1%al!pk5cX9Z`8J95 zt_qXct{-X)mk2s#Gps{N;>a;1F&d-Y$lfj0GWlL<)IUaumu}UVA8U?U7{6J!0CCqq z9vN&-9eW=a+N5h!PU$TmkrW#ce&^X%RoZ+F~T?ID_qB<7o;6)tE?w27|Os*&^xT@2LZzS)!=F9Rs>0^B|0u-B}( zNl0w@E%`{tV4q4{t{__9SVnWcNEc?!;cl=6y&*Vw9Pc07N2Ov@%v%!fnZhC)wX%C0%n=#QHv5J7TY8!vhxp{?=|zv7 zAEG-l>AX-1l3ws!-vLVLAv(vo8p4K)$v6X%<}{pS8vKc{%CQF|KZfD;Bq>oi=_`D21zg3JX3?P=l`+lVmBQ!pkr~VHokJ zkUjk=g6YEs30vQeuhMQF-A(SCx$7>Tpm87k%W?nw-!JliUfyGe0OQZm{Xfdg^EfER zKtCPu%<_~V)vqMSAQB}a7PZV%Qm;tm%IS*dkLUrQ>~{qqzMyjkBY?B%eG35?O&kW}0mXETeorvq1l6J1rIfv^TUGSBgSo70>;HXQrLxnw#l zzSR3fe*g)pStm&xV^_TOqpW~Evs)ooSiO^JRga^PsCScYkR|wtxxRc;A!_Y3S%%h> ziF!I)cB4pSS!2O`D93)MG6F7UigV8r6_L!_C@>`!<>O2(x?eG zS(xrKNzk#e2;SgykHF$k)tvEi)JQXqe+75%;zGtiDSmBypv(DEa%x+{Q1W0jS2^Ar z;YD~xkS_*DhM;Kax5gw4>v^vR`?{Bsf<_TIx!qdaz5peT)}_<+*GaY^MaJYf6k3+c z1VP?sheS}%x=20boUc{2NQYcrsn+u6g|QgUn7Xr=&95h=PS2`a&?ZI{Y+fTY;n6nF zc7mHHa6>*W)Exe8+i+#C=(_{jHdOrb>P_a~k1S=t>t9^Hbu0hz8K$a+N%ewu2@#`4 z3l9D>qu&b{8dyP8AW{qdY;4u+9>*O0!Pf1eASy#J(s!`$;MxT4huv5=k9xT05S8Fk zLV}SNK%VL!I9b1Z;9j^mJjM62nGYrvabBqxRa6r3P){+cB(b!c#E1{EA9C+!DM+(b zpZ4b-On~nwlXTihz8P~=*`>q)xkz4q&ZgwU5%)XD6s@2@2N4Y=qS?{wvuDmz`uS^; z9S^@prtP4EZ8BwWEjPltC?sv&m%_e!gGX31f*cO6kCtHR66>eBX?(4+7@=rPAs!^n z3spoM2EfOEfowchCdA?3?LF7Nvl)~lWA=t;HjA1*k2C~3OY`F6rva(4H#7;73O2hd zqSTbHq{@7Ug6b@kVXMpX?I+@xue3xr`7tM{>(pqa=9X0oSUxpQ3=hShumN9(NinFl$s?Q8J<@-6+ChwFU0UJCfs*;U-p3wK6*i}AC@um4L8yQV z-FS*mbw#A8CzujxFrLzM{h8e1v(#{DS$0d2g-2;uz>SIdW_QyfZfW-Ru;LWh%Th}z zr$(}3W%cmo*^E9w2k|l95$0#I`71Zc^YBZfNl&GI>=mER>y*IJl0EX*@3)38W31=~ zv4ujAYPVOElT}d?Bz$W}jS#G|d;0)Oe#}+DD?EgL)-kQr(2sUWB=@sMAKQnG#|7u(x2 z)M#MD`z668XwdFC)-^2vv=+pR_5hP*Z|e7EC;e|Sc%8KSi4e}OlI`}nzg)S0xpiNE zVnyI~LF5%`_%47>P?Tvx-pn4iEX~*`v9cdQ3Gf7GVZpetYI47%6yDJR$Gg_3#jBwM z#(yXZI*`c9x3a(R7}q;uV3i*C!&H#2MFsB?Jah-VTPg{$PNpyGAYE~K&_|saU3*pd zd6||7FO*H#WS{(r$rK~lXnF9-LD|WQ)r7UJiwUOTgDc-uTzAb6wHp>{L?uwmWf$8J zxR2V0yw4>)QfKg4G!ai4eRxQXU%W)F>B1@n=BxO-zs=t`91mx@sZ+zc=nxD2Vu4m~ zZYte|mCV@3kldi~wGh5GnIKHuJD?iJ&rj3A18zh<$PUuq(s&w+WzO7yB$XsgY8tg_ z7SUU^7u#70c~jRwPBjz<SJi3`odU zmq#fdmS}~iWq-w}7N=m$Vb9@WrM~ z{%r%(NO6`w6&H^H&up8LT@eHaiJ*{+-ay2}+_%Yw4KF!i6KTnT;t0g)7h!NonrhEY zddbMJq5{g5z-p={e2D-PBlLv>BXb*>vS63U5Q^0A1~)93xzR#IkZ6T$C7xny>tYbOh!m+CjB#s@$O&J}%2rvMwpjU51_{tnM&kfLv(F%N80N!> zVP}2xs$MuVKJlG8r`0aq>WLQ5o(l1JV;GE4z~nqX&tCVN9nKDZdc7uGYO10PZXO@= z@s{l6l6nxcb6Q7mkW+rJbB}ntX<+tJ?CD!Ei(XkoUP#rqMRfQ&oxVQIwY1^V`ssu| z7vwl|$rf4gI_t2;;%~G?i{Oqp?fHDP5SkfBi~;JOhg0-|wkH)bLT(9^Jx?}$Tks<{ z&nXBBMs$fB+hA342M<}RuV5j3j5x|17a5iIO4U_cYO|F(onU5Q9S&tJY^cx;0}m{f zsJ`xhI^R3X~j1MPVe+zPYsVBQw6SU!W%4f%#@2 zkG6br=Z)@*rW@lfC0>^oy(Q-;h{vhk5ibfRGp0(0H+y+(7v)#Kq2a$PN&A2Z{nXdd zstoxQ5nnuxrEDCggii_RS+x8vO5D8~*u?>;Ji6YorzD76-iwB@9qVDXJTnTej1hWi zM?u|WwAx&4>jD)h`g$}llxvrCMD&a4<4}eZkC8e2 zCepXI)#OPr^e9_{ zYd4Scc9b?M0?Jz1lkfc3fi&-&*qbxPfLgdLG8~pq1<>iZ$_`4dIZL(Me31@#^Hxb6 zwURj`a&pz#Z#Az4VXv19WtoC$un3pY5O3qhtj8$vZ^Lipbw{UEw$D5T8T(nke`NNn zn!9cjtETsmx>VAe>n)DGY(?0+mG@-BThH473ZckUtQ-)a>9LVXS)Z5%IOR&y_GN?$ zC*s+#d=a9DxHiygz;9mL?ZK+bl;j-y`Oc0 zvPu_k+{!kKw)47^1rj0BX z@zvAzPeR^{BqoO}bT5e8rSTAOBOYQ6SGveRQqE0;Be%zu+vW}!wJ z*GFPOUqaXO4arQg?Zj?+4mo#CMpbAcBXxP$07>Q1O-$9^sPFY=Hcsx4O9L+TIU^raS#^ovwxDwoPDB(vMdHzNV1yxNs zwT0D=68C7?L}bU3t+3}r*wjmhis;f+eVL-()6%cwdi3dMrKhrSR#{CK*G(gwBI9;h zG&F~-op}z=mcpJr8hVw6+$Ia;umjKWAPEXiO>=HmvtHelBsjtNGLF6jTazN?UQEh> z*R7gWALMr8?S)e%Fikr#R7s;9dj;uG@a;msE07M;{L+m7!r-wt`>qL-3;{Bmv8h-Z z3di;%JyzsXQTNmj(OPJVS7hiZJ0F^NHB-)O$Twv>>kD*7Rlh=h!!orwe{1@drC;^GUBR&u5qtIFNF(8ji_75OmnK6P4q3 zCE^BD<~IPPp(|@`rjVx;HDp_xw}x( z7%FkWhm!4e4Ly@*8KNAoqs#wBuR-ouM?bY~-Lna&)8@xdMRcOAurIjB)H1~Hc7&|{ zLTOd$yK9>8IRNwWWuYOrWq5+ac^-X}WHl9g>e1Sf9^d5K+hZb+OsWjRHYxLYmDQt0 zXzNU*3vJa8sYR0QV5w?%=4E zN?&Rbk>-u)qG>uT{m_YTr|yV=n3{U^sbx&F-m)DRK&u$S%~kGs zTH$)RCwi%PJvT>B2%>VFUw-ZsJ|ea|LgORx>|rQDNS8OG&*&cTl2ctYk-maGV)*{l zv$HFM!fJ8-T=Vi3`PG5bIn*FYm%^pn>|U;%;sMe*Mh1b&P%(G7$L8r)fpf;^8wlA; z^wp7#QQ~XTb+$`;U-tFv8o<>ie(Er}K*HC#xSjk+#e*l@eCGw&vucjttCh=deLQPM zjh~b$LzTz#oGyRL3vP^rn93<#=#2rB3Voyka776e4|et;InBp7#BIjKh~^I^pbFw* z2|GjYx#4AAtm_IvN>N|Dx3(JCw>HiThEc&YhW4{z ziN+s?4tWAr_*UPsyxi_>7*LygZXy^_JmmX$#U0h0GR3ANlci70c?Bb3>R1#>iIjAq(S{mMok@b!UR&rJGT z!}ajGkq%L`+k4r*bERW&J_(H=9F%URu;XHA+qUJexjGD(_b0VQ`W%rci!{rgl7!dY974z_%*3gps|ODyecqNgmTxu+K3iNgXAJxf6EE zIW@ei=IR5ddbn$YESSluDwtBfC-&&;5;-({8s{PC)!25X1pthkSe5eF)heGVWp!<# z2Klm2UBH3FLiXYk>hf)k1jo2(6Fir&U&s6}RggF7(@MR+Q=+b8>R6eY~V* zqnNH5BR*k_bSTAWAi=xC^Y%_gpqJ86!QAc^~^Z4Ps*iwxC7UZKqX z`NDU`=UMisO?a@SRa~6b&9RGLuti~UhoXYCr=nE0Zay5PY zBs60NHz?mxeH?s~AnqWm>bl@D8LG}_K7E(hwbBgMJN)05m;|g;WJWTNIpWm4vdn`Q zzKUQbYI%f9>bN9pRX^c1Z>0vsv9THMkMAH^69^b`dGwZVke zXqVcM50=?#K24Y*ZED#fOPCus=jKxw^dU>&T^VMhON^LMz}+vbR(rp-zfcu#0ArAg zPP;--pt@l}T8paV*uQ;B1SW6$n*6grN zT_-8%{EPgSIU>?VpzkpCt>@ciw1ey4{GQmSudb_*!N7o2zq+US+cS~h4nhq72(P|l zy8Hc1q)f%^jw{&X9p+%4Z+iqY6|9(UTU8W&ZImux1p>99F*pUs~&uk(wa z>12FgwE}zcH4+69@{*o6aVpf+c=QG1=AanyO$!OVgB88LW*fy4t+d?JP~E z-H@H(fW+K#3ZzigYJ37sxsNa%*63-SbOyw<%rQjAb1G6oGMchB9n)%EvU_i9_{!1Z zP1kUI;zmRS$0xj0HmR}kJ$9+>dh@3&@cFEC73}f`OpDmH9s*Vfr^B$)=er1RI1oJ` zU+82p)4mo#5eW>CnI=J&J{}gWP|mc(*n@o!e6g3aA<_#CGhad+mJhRMRY4*uKfkWA zJ5m8Y3gZYjUv18=KX(}t_AI3Sb)BYfKsfz$s0buK#BO-I*@mb>=1iPjZxs{|+Ix0) zS?6tE`WIQxd|E;h8?_M4c1-%9jHNPjma@dseNphP`SLiKaN6~}JDo^7sGekz4#2s+ z>=fprK_0>>(YGjpmmjEv@{P$M_6~QzMM3y9nL=BD>5h?u5;mdE8veBBfC){DF4jK~ zHJpsC{G5qAnc&j_j4X@@=E)e4Bz}vVb})!oHZgG+_Y@~tz}R4HVB>;&fn#-E6M;LF zVtL*(5b6U-uo^}T&vl5O^2$^9@^3v=$Riado%qDxk0R@g-0xV;LoCrR;U0_@J@C z>uGtz(a|tb@8>iOlvwP1!F)DSweafR0)+G7bdp3}O1UJCqPDt*NI)cByZP2$V>UNM|uud8-v z-64JmvjGO)LY#6_cfodFPZrAh3%xuD_Jl$+F9Q_;Io?g>l+%m-3#qRb@E%0G>!GEO zS`}F?6WL$&z@@5w9*}uDDAqC?#CszTL)OX#ITQ9}_?mRhCm#DTY)s9PDE0(W$SC(`6j zZ-co==Vd&6!B9M`$+dn}z+<(_kW@5;*F%8Kc z_rTY}>*1bvz+bomfD)PNYATayfBuov(FS3z3->J`KSGJHhQQW zm+?%nE*$Dl@ld%WwmS`dP`x*fDSIp8&ocBIZ#tZTx*=nh>$wpgSxI2uXFYwsj!|Fiuivcw=)!HRLSB{Gx-<@~n!QqZ z#bNhJEVwX-OYn5C*?`inLYhIC{gvcZ0eYf^8$lu(AI8@@`i6bz^z=j#mZ^1!dKGfU zVuXm;7#paZasHS7qdg+&@_^P*tYRe(xdu=F9OTyb_Lpz+hRZM<2vQ|uViE@X z)XMpMDn@W9HkHfr-Kx)+ZsOY0W200)HB38EAwE9JR)x*<)g@1QE;C`f&khyo>7YG9 z?xRGIdkMRH0tSwsB6)*02Uy{Sg#dnHP8!Ler-$cGa9u){}=A&D)}f6^Xnu1jgvk5Ou%ju$#HX z@C<&+l_|L#J)ng`K4cA<0L+$vr+(kSlOC2C#8cvHfqsXT(&D!R52(@44LTKIW9 z&s?K0TJx}M$37;8NcA?;UF(MM?t&qRc>Vb{G#HpGXhHqoP7gePcSZN7#q@W_p5K?$ zv^$rcJD=eM0JW4igmOzRjF2XfHsmA+L$u2;7bQ03sWa}ZM3Z5YWvwRqZLmP<`I0XM zjUejD453kTbraA(087Wwac|yjuK`3{d2zK&>4i~Bd%#>eRTk2N+pL745l#rB=w^8+ zCak8>KT?A=Zys_a_FiS#nEPF-ev{s|gQB39o^uAF_0U&i(YeoaSmde1&TZidreo@# zxh-ZIvsO>?(~LG4H!x!7=%twG-trEw@~T12jSWdUhD-WzFHG#RLwk~_8^Tyj43Z!` zgH}E!E!7Ru13m%*)URJ=`=hk$KEuwYxkNU^j`@&LXYSVF+JA;Xf;{v|YM#ngD$$J* zyP|~0=Htq(IBGU-F-#K`lrFXunVUEqTAl=kVp9G*jg@Ny+kCkXEy$NWguW9Q1AuM; z2p!@iUj)Js%Sr&6oEsQYY^njhC0$IzL!I?GZ+OCRUd3O2U=5>ml^_d!R3AVN6^amD zU6)DXP1Zj$@ud-1E2L(ebi{+Y>|ACv?b?Y9s5aKnUw9cEAO^+OvePih-?$xC>J!fz zVACH(ElWFliv?cC4|P}X4An~j;&!Z@?eP?NuYi%L+i!l3o&Ofr|; z)tY=*7~}O(2m1R4_1DvZ2#Z4RjpDmlwOoxaA$W7ivDY?wZjPs6w0NRb{2c}SOnY+! zH+i2&Q^s|h;>+R-%A^rh+4(J6VP7m6MvieVeGMb^!VWOS&q>>w8ev#FuJ;=x(C+LU z%xy7P;)j-FszyuW@0fo#p&Eu~;0?I&#ga`6xaqCm>$IA`p5J>)n%)LkncfAHZ{z8cLT!f? z7+w>pxMXWfwbk?`EL5zwbQ#dMU5E#fpO}luPRNyVUBvgWT(01H-PDQ8{2Hh<9!T zUsa*7eD#3U^poU!)1b#rv13vnn4Vy!(Gj7gkQmPDiz-t#Ts9VgQ!$R)pSdp$ThJrZ zy2-|~NOqVO5L*c&_R0!%K#P5h;5Mco3E$)OxiJgL6WufKl@&|lGhKtx&#y`h9S#p* z^Tbo>GA#^<=>hsPJp&WE4&>dcl^njftX!&Eo=L(^Etw5+z!Y!5aL!foh9mT)0ReyC zbJ(V$*ZcT)y}vJH85jieZ(#qWTcr5k_5Q=eZ}+}Q9#O7&!@Zy06ttL}UY%QEH3Stw> zQf&xDZC_&;N!AS@bzD#%c<|vW943zxN5W2sY6AC-P-R)bD^YMMS~Zd2ij*zJ-bJqy zIcAuom)kUQkZ-b#Qa*-=vc?3zS3GMq;Uz1*y0+clRJO}lM6Z@_a)Oi8bfrV=dI zG~}ijJz9lVr=Z~rH8cl8*y%Kzj_4}BD+YM>Y#{)KzY1CIe#C1$fu?WHuE9GVY z(oY&lK|24V!BWrB2=FKP`-O3SDy;wK!e&+s_Ij`NY|VbDhVmyhCBIVhTb<~gZ1t?I zjcosuw=WZKvX9)J6ltO^o`=DX}t=rE^t*tB>tZl78`t8k(?0#iCkjK(J$pArE z*_!;RQg{FI!`dK*se3a1M+rS^Jp)stUlv5UR}2j731~FkLH$wi-*%MTUlsq!rjLFf zrFXdj#-^`(gg`5oE*u!xT{^WN0tCOy!t|$F{7@rgWo3VtC%{@p&kO(xm;7&bfZr^7 z4}g6~I2#pYiB*s~mLJ+dParri=&ksl03t@ldJY!$A|QSR3oAWC5G5Y-?>otd`Ui1! z;9x=etwG(T_>=xJPF{-;WryUFd3L|}JA^slXOKb5+`Ps+tX^UVKL{!-80RM5`O$Wk9< z2{LIb13e27Gtk>$rtk1yTIz=lxt|>tWQ_j^5FEhwPqF^G758%`-es5lAwclQBEQi5 zaJ>JNYxZI7@26$^d74lJv0MI6Oa0LUpe@Y99E=YE?x#Yz%kK6=fZ);~=g_|c_&L|x zZ@T}-N_>}0<-fwM@(bN}sZ}0U^M2}wJMQuy0t65EJ5_(5SmhzueF}AumH#6^@B{U~ zsrL`CfATr;5cWRt_s?y_(D@tKd)wCk!Pfo|>^^Dr9hdkI0fJBI{&TPgd*p{8_i0-1 zE(LxF5Ij)-pM%^#&v=M%pJejquDUe&=Lo+$X8wZw^&#wiWK JS$+5G{{hr`vzY(@ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 642d572ce..44f3cf2c1 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/mvnw b/mvnw index 41c0f0c23..e9cf8d330 100755 --- a/mvnw +++ b/mvnw @@ -19,292 +19,277 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.3 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac -fi +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 fi fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done + printf %x\\n $h +} - saveddir=`pwd` +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - M2_HOME=`dirname "$PRG"`/.. +die() { + printf %s\\n "$1" >&2 + exit 1 +} - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" fi -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi +mkdir -p -- "${MAVEN_HOME%/*}" -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; fi -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi fi -########################################################################################## -# End of extension -########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 86115719e..3fd2be860 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,182 +1,189 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.3 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index fa47288ae..60ceafefb 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - classgraph-4.8.181 + classgraph-4.8.182 @@ -58,13 +58,13 @@ io.github.toolfactory narcissus - 1.0.7 + 1.0.11 true io.github.toolfactory jvm-driver - 9.7.1 + 9.9.10 true @@ -429,6 +429,10 @@ org.apache.maven.plugins maven-surefire-plugin + + + --enable-native-access=ALL-UNNAMED + diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 3c3b9baab..365cc9f86 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -98,12 +98,6 @@ public enum CircumventEncapsulationMethod { * private classloader fields or methods in order to determine the classpath. */ NARCISSUS, - - /** - * Use the JVM-Driver library to try to gain access - * to private classloader fields or methods in order to determine the classpath. - */ - JVM_DRIVER; } /** @@ -112,18 +106,13 @@ public enum CircumventEncapsulationMethod { * method or field. * *

      - * To enable a workaround to this, set this static field to {@link CircumventEncapsulationMethod#NARCISSUS} or - * {@link CircumventEncapsulationMethod#JVM_DRIVER} before interacting with ClassGraph in any other way, and - * also include the Narcissus or - * JVM-Driver library respectively on the classpath or - * module path. + * To enable a workaround to this, set this static field to {@link CircumventEncapsulationMethod#NARCISSUS} + * before interacting with ClassGraph in any other way, and also include the + * Narcissus library on the classpath or module path. * *

      * Narcissus uses JNI to circumvent encapsulation and field/method access controls. Narcissus employs a native * code library, and is currently only compiled for Linux x86/x64, Windows x86/x64, and Mac OS X x64 bit. - * - *

      - * JVM-Driver uses a pure JVM solution to try to circumvent encapsulation and security controls. */ public static CircumventEncapsulationMethod CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NONE; diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java b/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java deleted file mode 100644 index 854102319..000000000 --- a/src/main/java/nonapi/io/github/classgraph/reflection/JVMDriverReflectionDriver.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Luke Hutchison - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without - * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO - * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ -package nonapi.io.github.classgraph.reflection; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Reflection driver for the jvm-driver library, if it is - * available at runtime. This library allows access to non-public fields and methods, circumventing encapsulation - * and visibility controls, via a range of JVM-native tactics. - */ -class JVMDriverReflectionDriver extends ReflectionDriver { - private Object driver; - private final Method getDeclaredMethods; - private final Method getDeclaredConstructors; - private final Method getDeclaredFields; - private final Method getField; - private final Method setField; - private final Method invokeMethod; - private final Method setAccessibleMethod; - - private static interface ClassFinder { - Class findClass(String className) throws Exception; - } - - private ClassFinder classFinder; - - JVMDriverReflectionDriver() throws Exception { - // Construct a jvm-driver Driver instance via reflection, so that there is no runtime dependency - final StandardReflectionDriver drv = new StandardReflectionDriver(); - // driverClass = drv.findClass("io.github.toolfactory.jvm.Driver"); - // Class driverFactoryClass = drv.findClass("io.github.toolfactory.jvm.Driver$Factory"); - // driver = drv.invokeStaticMethod(drv.findMethod(driverFactoryClass, "getNew")); - // if (driverInstance == null) { - // throw new IllegalArgumentException("Could not load jvm-driver library"); - // } - final Class driverClass = drv.findClass("io.github.toolfactory.jvm.DefaultDriver"); - for (final Constructor constructor : drv.getDeclaredConstructors(driverClass)) { - if (constructor.getParameterTypes().length == 0) { - driver = constructor.newInstance(); - break; - } - } - if (driver == null) { - throw new IllegalArgumentException("Could not instantiate jvm.DefaultDriver"); - } - - // Look up needed methods - getDeclaredMethods = drv.findInstanceMethod(driver, "getDeclaredMethods", Class.class); - getDeclaredConstructors = drv.findInstanceMethod(driver, "getDeclaredConstructors", Class.class); - getDeclaredFields = drv.findInstanceMethod(driver, "getDeclaredFields", Class.class); - getField = drv.findInstanceMethod(driver, "getFieldValue", Object.class, Field.class); - setField = drv.findInstanceMethod(driver, "setFieldValue", Object.class, Field.class, Object.class); - invokeMethod = drv.findInstanceMethod(driver, "invoke", Object.class, Method.class, Object[].class); - setAccessibleMethod = drv.findInstanceMethod(driver, "setAccessible", AccessibleObject.class, - boolean.class); - try { - // JDK 7 and 8 - final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, boolean.class, - ClassLoader.class); - classFinder = new ClassFinder() { - @Override - public Class findClass(final String className) throws Exception { - return (Class) forName0_method.invoke(null, className, true, - Thread.currentThread().getContextClassLoader()); - } - }; - } catch (final Throwable t) { - // Fall through - } - if (classFinder == null) { - try { - // JDK 16 (and possibly earlier) - final Method forName0_method = findStaticMethod(Class.class, "forName0", String.class, - boolean.class, ClassLoader.class, Class.class); - classFinder = new ClassFinder() { - @Override - public Class findClass(final String className) throws Exception { - return (Class) forName0_method.invoke(null, className, true, - Thread.currentThread().getContextClassLoader(), JVMDriverReflectionDriver.class); - } - }; - } catch (final Throwable t) { - // Fall through - } - } - if (classFinder == null) { - try { - // IBM Semeru - final Method forNameImpl_method = findStaticMethod(Class.class, "forNameImpl", String.class, - boolean.class, ClassLoader.class); - classFinder = new ClassFinder() { - @Override - public Class findClass(final String className) throws Exception { - return (Class) forNameImpl_method.invoke(null, className, true, - Thread.currentThread().getContextClassLoader()); - } - }; - } catch (final Throwable t) { - // Fall through - } - } - if (classFinder == null) { - // Fallback if the above fails: just use Class.forName. - // This won't find private non-exported classes in other modules. - final Method forName_method = findStaticMethod(Class.class, "forName", String.class); - classFinder = new ClassFinder() { - @Override - public Class findClass(final String className) throws Exception { - return (Class) forName_method.invoke(null, className); - } - }; - } - } - - @Override - public boolean makeAccessible(final Object instance, final AccessibleObject accessibleObject) { - try { - setAccessibleMethod.invoke(driver, accessibleObject, true); - } catch (final Throwable t) { - return false; - } - return true; - } - - @Override - Class findClass(final String className) throws Exception { - return classFinder.findClass(className); - } - - @Override - Method[] getDeclaredMethods(final Class cls) throws Exception { - return (Method[]) getDeclaredMethods.invoke(driver, cls); - } - - @SuppressWarnings("unchecked") - @Override - Constructor[] getDeclaredConstructors(final Class cls) throws Exception { - return (Constructor[]) getDeclaredConstructors.invoke(driver, cls); - } - - @Override - Field[] getDeclaredFields(final Class cls) throws Exception { - return (Field[]) getDeclaredFields.invoke(driver, cls); - } - - @Override - Object getField(final Object object, final Field field) throws Exception { - return getField.invoke(driver, object, field); - } - - @Override - void setField(final Object object, final Field field, final Object value) throws Exception { - setField.invoke(driver, object, field, value); - } - - @Override - Object getStaticField(final Field field) throws Exception { - return getField.invoke(driver, null, field); - } - - @Override - void setStaticField(final Field field, final Object value) throws Exception { - setField.invoke(driver, null, field, value); - } - - @Override - Object invokeMethod(final Object object, final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(driver, object, method, args); - } - - @Override - Object invokeStaticMethod(final Method method, final Object... args) throws Exception { - return invokeMethod.invoke(driver, null, method, args); - } -} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java index 176a85efa..2d0f974fa 100644 --- a/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/reflection/ReflectionUtils.java @@ -54,13 +54,6 @@ public ReflectionUtils() { System.err.println("Could not load Narcissus reflection driver: " + t); // Fall back to standard reflection driver } - } else if (ClassGraph.CIRCUMVENT_ENCAPSULATION == CircumventEncapsulationMethod.JVM_DRIVER) { - try { - reflectionDriver = new JVMDriverReflectionDriver(); - } catch (final Throwable t) { - System.err.println("Could not load JVM-Driver reflection driver: " + t); - // Fall back to standard reflection driver - } } if (reflectionDriver == null) { reflectionDriver = new StandardReflectionDriver(); diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java index aeb62c19a..cffa0bf68 100644 --- a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -23,7 +23,7 @@ void resetAfterEachTest() { /** Test Narcissus. */ @Test void testNarcissus() { - ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS; + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsculationMethod.NARCISSUS; final ReflectionUtils reflectionUtils = new ReflectionUtils(); assertThat( reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass().getSimpleName()) @@ -34,19 +34,4 @@ void testNarcissus() { assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); } } - - /** Test JVM-Driver. */ - @Test - void testJVMDriver() { - ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.JVM_DRIVER; - final ReflectionUtils reflectionUtils = new ReflectionUtils(); - assertThat( - reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass().getSimpleName()) - .isEqualTo("JVMDriverReflectionDriver"); - try (ScanResult scanResult = new ClassGraph() - .acceptPackages(EncapsulationCircumventionTest.class.getPackage().getName()).enableAllInfo() - .scan()) { - assertThat(scanResult.getAllClasses().getNames()).isNotEmpty(); - } - } } From 1534463a334aaf8eabc7193cc3c8e3efed4a169c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 02:14:44 -0600 Subject: [PATCH 709/713] Remove EOL JVM-Driver support (Narcissus is still supported) --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 60ceafefb..479e0e5b0 100644 --- a/pom.xml +++ b/pom.xml @@ -61,12 +61,6 @@ 1.0.11 true - - io.github.toolfactory - jvm-driver - 9.9.10 - true - From 6f60747cedc02cb698672c87572c1c460e38f440 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 02:29:28 -0600 Subject: [PATCH 710/713] Bump minimum JDK version to 8 --- pom.xml | 31 +++++-------------- .../java/io/github/classgraph/ClassGraph.java | 2 +- .../java/io/github/classgraph/ClassInfo.java | 8 ++++- .../classgraph/CloseableByteBuffer.java | 2 ++ .../classgraph/HierarchicalTypeSignature.java | 5 +++ .../io/github/classgraph/ModulePathInfo.java | 4 +++ .../EncapsulationCircumventionTest.java | 2 +- .../issues/issue289/Issue289Test.java | 4 --- .../issues/issue345/Issue345Test.java | 3 -- 9 files changed, 27 insertions(+), 34 deletions(-) diff --git a/pom.xml b/pom.xml index 479e0e5b0..14eaf4c26 100644 --- a/pom.xml +++ b/pom.xml @@ -369,34 +369,19 @@ - + org.apache.maven.plugins maven-compiler-plugin UTF-8 - - - - - - - - - - - - - 7 - 7 - - 8 - 8 + + 8 false - -Xlint:all - -Xlint:-options - -Werror + -Xlint:all + -Xlint:-options + -Werror @@ -408,9 +393,7 @@ UTF-8 - - 8 - 8 + 8 -parameters diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 365cc9f86..d77c06a8b 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -86,7 +86,7 @@ public class ClassGraph { * Method to use to attempt to circumvent encapsulation in JDK 16+, in order to get access to a classloader's * private classpath. */ - public enum CircumventEncapsulationMethod { + public static enum CircumventEncapsulationMethod { /** * Use the reflection API and {@link AccessibleObject#setAccessible(boolean)} to try to gain access to * private classpath fields or methods in order to determine the classpath. diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index c85ee2f20..59e87137a 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2797,6 +2797,8 @@ public FieldInfoList getFieldInfo() { } /** + * Get the enum constants of an enum class. + * * @return All enum constants of an enum class as a list of {@link FieldInfo} objects (enum constants are stored * as fields in Java classes). */ @@ -2812,7 +2814,11 @@ public boolean accept(final FieldInfo fieldInfo) { }); } - /** @return All enum constants of an enum class as a list of objects of the same type as the enum. */ + /** + * Get the enum constants of an enum class. + * + * @return All enum constants of an enum class as a list of objects of the same type as the enum. + */ public List getEnumConstantObjects() { if (!isEnum()) { throw new IllegalArgumentException("Class " + getName() + " is not an enum"); diff --git a/src/main/java/io/github/classgraph/CloseableByteBuffer.java b/src/main/java/io/github/classgraph/CloseableByteBuffer.java index 3762ca9aa..316685576 100644 --- a/src/main/java/io/github/classgraph/CloseableByteBuffer.java +++ b/src/main/java/io/github/classgraph/CloseableByteBuffer.java @@ -55,6 +55,8 @@ public class CloseableByteBuffer implements Closeable { } /** + * Get the wrapped ByteBuffer. + * * @return The wrapped {@link ByteBuffer}. */ public ByteBuffer getByteBuffer() { diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index fe54eb0ae..d3f6bb8a2 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -37,6 +37,11 @@ */ public abstract class HierarchicalTypeSignature extends ScanResultObject { protected AnnotationInfoList typeAnnotationInfo; + + /** A hierarchical type signature. */ + public HierarchicalTypeSignature() { + super(); + } /** * Add a type annotation. diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 3992c2048..9700ae5e9 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -126,6 +126,10 @@ public class ModulePathInfo { '\0', // --add-opens (only one param per switch) '\0' // --add-reads (only one param per switch) ); + + /* Module path info. */ + public ModulePathInfo() { + } /** Set to true once {@link #getRuntimeInfo()} is called. */ private final AtomicBoolean gotRuntimeInfo = new AtomicBoolean(); diff --git a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java index cffa0bf68..2acd456a7 100644 --- a/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java +++ b/src/test/java/io/github/classgraph/features/EncapsulationCircumventionTest.java @@ -23,7 +23,7 @@ void resetAfterEachTest() { /** Test Narcissus. */ @Test void testNarcissus() { - ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsculationMethod.NARCISSUS; + ClassGraph.CIRCUMVENT_ENCAPSULATION = CircumventEncapsulationMethod.NARCISSUS; final ReflectionUtils reflectionUtils = new ReflectionUtils(); assertThat( reflectionUtils.getFieldVal(true, reflectionUtils, "reflectionDriver").getClass().getSimpleName()) diff --git a/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java b/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java index ae875e683..5e188a3c6 100644 --- a/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java +++ b/src/test/java/io/github/classgraph/issues/issue289/Issue289Test.java @@ -15,12 +15,8 @@ * Issue289. */ public class Issue289Test { - /** * Issue 289. - * - * @throws Exception - * the exception */ @Test public void issue289() { diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java index ae8a82129..32c9aa186 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345Test.java @@ -102,9 +102,6 @@ public void testNonExtensionToInnerClass() { /** * Test that overriding classloaders does not allow other classloaders to be scanned. - * - * @throws Exception - * the exception */ @Test public void issue345b() { From 4024fbf2f178c7e19af186eef9cdfbf80cc9f995 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 02:51:53 -0600 Subject: [PATCH 711/713] Build fix --- pom.xml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 14eaf4c26..a6a34e04d 100644 --- a/pom.xml +++ b/pom.xml @@ -407,8 +407,8 @@ org.apache.maven.plugins maven-surefire-plugin - - --enable-native-access=ALL-UNNAMED + + ${surefireArgLine} @@ -513,15 +513,24 @@ jdk9plus + + [9,) + -html5 true + + + jdk17plus - [9,) + [17,) + + --enable-native-access=ALL-UNNAMED + From 11cd1aae71922c2890890f18bd239a910e5d5d43 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 03:20:18 -0600 Subject: [PATCH 712/713] Probably fix #916 --- pom.xml | 2 +- .../classgraph/fastzipfilereader/NestedJarHandler.java | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a6a34e04d..b62fe96e2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.182 + 4.8.183 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java index 393d9e5fd..2bad6e807 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -687,11 +687,14 @@ public void close() { * Signals that an I/O exception has occurred. */ public InputStream openInflaterInputStream(final InputStream rawInputStream) throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } + @SuppressWarnings("resource") + final RecyclableInflater recyclableInflater = inflaterRecycler.acquire(); + final Inflater inflater = recyclableInflater.getInflater(); return new InputStream() { // Gen Inflater instance with nowrap set to true (needed by zip entries) - @SuppressWarnings("resource") - private final RecyclableInflater recyclableInflater = inflaterRecycler.acquire(); - private final Inflater inflater = recyclableInflater.getInflater(); private final AtomicBoolean closed = new AtomicBoolean(); private final byte[] buf = new byte[INFLATE_BUF_SIZE]; private static final int INFLATE_BUF_SIZE = 8192; @@ -1081,7 +1084,6 @@ public void close(final LogNode log) { } if (inflaterRecycler != null) { inflaterRecycler.forceClose(); - inflaterRecycler = null; } // Temp files have to be deleted last, after all PhysicalZipFiles are closed and // files are unmapped From 49a64e78cbe278b68949b57a9ece2ea2831af479 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Oct 2025 03:32:54 -0600 Subject: [PATCH 713/713] Bump version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b62fe96e2..f19d6afaf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.183 + 4.8.184 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages.